From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- .../main/java/com/github/cereda/arara/Arara.java | 205 +++ .../arara/controller/ConfigurationController.java | 106 ++ .../arara/controller/LanguageController.java | 120 ++ .../cereda/arara/controller/LoggingController.java | 109 ++ .../cereda/arara/controller/SessionController.java | 124 ++ .../arara/controller/SystemCallController.java | 153 +++ .../github/cereda/arara/model/AraraException.java | 88 ++ .../com/github/cereda/arara/model/Argument.java | 125 ++ .../com/github/cereda/arara/model/Command.java | 122 ++ .../com/github/cereda/arara/model/Conditional.java | 139 ++ .../github/cereda/arara/model/Configuration.java | 246 ++++ .../com/github/cereda/arara/model/Database.java | 80 ++ .../com/github/cereda/arara/model/Directive.java | 146 +++ .../com/github/cereda/arara/model/Evaluator.java | 158 +++ .../com/github/cereda/arara/model/Extractor.java | 88 ++ .../com/github/cereda/arara/model/FileType.java | 170 +++ .../cereda/arara/model/FileTypeResource.java | 84 ++ .../com/github/cereda/arara/model/Interpreter.java | 458 +++++++ .../com/github/cereda/arara/model/Language.java | 148 +++ .../com/github/cereda/arara/model/Messages.java | 170 +++ .../java/com/github/cereda/arara/model/Pair.java | 92 ++ .../java/com/github/cereda/arara/model/Parser.java | 410 ++++++ .../com/github/cereda/arara/model/Resource.java | 291 +++++ .../java/com/github/cereda/arara/model/Rule.java | 155 +++ .../com/github/cereda/arara/model/RuleCommand.java | 103 ++ .../com/github/cereda/arara/model/Session.java | 122 ++ .../com/github/cereda/arara/model/StopWatch.java | 84 ++ .../com/github/cereda/arara/model/Trigger.java | 135 ++ .../cereda/arara/utils/ClassLoadingUtils.java | 166 +++ .../com/github/cereda/arara/utils/CommonUtils.java | 929 ++++++++++++++ .../cereda/arara/utils/ConfigurationUtils.java | 239 ++++ .../github/cereda/arara/utils/DatabaseUtils.java | 140 ++ .../cereda/arara/utils/DirectiveAssembler.java | 106 ++ .../cereda/arara/utils/DirectiveResolver.java | 59 + .../github/cereda/arara/utils/DirectiveUtils.java | 438 +++++++ .../github/cereda/arara/utils/DisplayUtils.java | 502 ++++++++ .../cereda/arara/utils/FileHandlingUtils.java | 124 ++ .../cereda/arara/utils/FileSearchingUtils.java | 117 ++ .../cereda/arara/utils/InterpreterUtils.java | 257 ++++ .../github/cereda/arara/utils/MessageUtils.java | 313 +++++ .../com/github/cereda/arara/utils/Methods.java | 1356 ++++++++++++++++++++ .../com/github/cereda/arara/utils/RuleUtils.java | 244 ++++ .../github/cereda/arara/utils/TeeOutputStream.java | 109 ++ .../com/github/cereda/arara/utils/UnsafeUtils.java | 87 ++ .../github/cereda/arara/utils/VelocityUtils.java | 157 +++ 45 files changed, 9774 insertions(+) create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/Arara.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/controller/ConfigurationController.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/controller/LanguageController.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/controller/LoggingController.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/controller/SessionController.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/controller/SystemCallController.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/AraraException.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Argument.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Command.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Conditional.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Configuration.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Database.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Directive.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Evaluator.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Extractor.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/FileType.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/FileTypeResource.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Interpreter.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Language.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Messages.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Pair.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Parser.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Resource.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Rule.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/RuleCommand.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Session.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/StopWatch.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/model/Trigger.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/ClassLoadingUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/CommonUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/ConfigurationUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/DatabaseUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveAssembler.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveResolver.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/DisplayUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/FileHandlingUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/FileSearchingUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/InterpreterUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/MessageUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/Methods.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/RuleUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/TeeOutputStream.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/UnsafeUtils.java create mode 100644 support/arara/source/src/main/java/com/github/cereda/arara/utils/VelocityUtils.java (limited to 'support/arara/source/src/main/java') diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/Arara.java b/support/arara/source/src/main/java/com/github/cereda/arara/Arara.java new file mode 100644 index 0000000000..c644978c93 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/Arara.java @@ -0,0 +1,205 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara; + +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.controller.LoggingController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Configuration; +import com.github.cereda.arara.model.Directive; +import com.github.cereda.arara.model.Extractor; +import com.github.cereda.arara.model.Interpreter; +import com.github.cereda.arara.model.Parser; +import com.github.cereda.arara.model.StopWatch; +import com.github.cereda.arara.utils.CommonUtils; +import com.github.cereda.arara.utils.DirectiveUtils; +import com.github.cereda.arara.utils.DisplayUtils; +import java.util.List; + +/** + * Main class. This class wraps all classes from the application model as well + * as utilitary classes in order to provide modularity. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Arara { + + /** + * Main method. This is the application entry point. + * @param args A string array containing all command line arguments. + */ + public static void main(String[] args) { + + // the first component to be initialized is the + // language controller; note that init() actually + // has no body at all, but it's a dirty maneuver to + // trigger the static class startup + LanguageController.init(); + + // the second component to be initalized is the + // logging controller; note init() actually disables + // the logging, so early exceptions won't generate + // a lot of noise in the terminal + LoggingController.init(); + + // 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(); + + 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(); + + // if we are here, either there was no configuration + // file at all or we managed to load the settings; now, + // it's time to properly parse the command line arguments; + // this is done by creating a brand new instance of arara's + // command line parser and providing the string array to it + Parser parser = new Parser(args); + + // now let's see if we are good to go; parse() will return + // a boolean value indicating if the provided arguments + // allow the tool to continue (we might reach some special + // flags as well, like --help or --version, which simply + // do their jobs and return false, since there's no point + // of continuing processing with such flags) + if (parser.parse()) { + + // 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; + // this class does a pretty good job on finding directives, + // including the multiline ones; it was a long awaited + // feature people were asking me to implement, so here + // it is! + Extractor extractor = new Extractor(); + + // 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) + List directives = extractor.extract(); + + // once we have our nice list of directives, it is time to + // actually validate them (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 + directives = DirectiveUtils.validate(directives); + + // arara features now a cool stopwatch, so we can see how + // much time has passed since everything started; start(), + // for obvious reasons, starts the stopwatch and keeps track + // of time for us; 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) + StopWatch.start(); + + // this is surely the golden heart of arara; this class + // implements a powerful interpreter that will handle all + // rules and their corresponding tasks + Interpreter interpreter = new Interpreter(); + + // once we have this bad boy ready, let's provide the list + // of directives previously extracted and validated; it is + // like loading a cannon, I guess + interpreter.setDirectives(directives); + + // 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.execute(); + } + + } catch (AraraException exception) { + + // 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); + } + + // we are done here (with or without errors, that makes no difference + // at this point), so let's stop our stopwatch; now it's just an easy + // subtraction to be made (note that the values are internally + // represented as nanoseconds, but the result is printed as seconds) + StopWatch.stop(); + + // 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(); + + // 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. + System.exit(CommonUtils.getExitStatus()); + + // I owe David Carlisle $10 + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/controller/ConfigurationController.java b/support/arara/source/src/main/java/com/github/cereda/arara/controller/ConfigurationController.java new file mode 100644 index 0000000000..f3868b488f --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/controller/ConfigurationController.java @@ -0,0 +1,106 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.controller; + +import java.util.HashMap; +import java.util.Map; + +/** + * Implements the configuration controller. 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 Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class ConfigurationController { + + // the controller itself, since we have a singleton; + // this is the reference instance, instantiated once + private static final ConfigurationController instance = + new ConfigurationController(); + + // the configuration settings are stored in a map; + // pretty much everything can be stored in this map, + // as long as you know what to retrieve later on + private final Map map; + + /** + * Private constructor. Called once for creating the proper singleton. + */ + private ConfigurationController() { + map = new HashMap(); + } + + /** + * Gets the current configuration controller. Since this class is + * implemented as a singleton, you will get the same controller every + * single time, and that's good. + * @return The configuration controller, which hold the settings map. + */ + public static ConfigurationController getInstance() { + return instance; + } + + /** + * Returns the object indexed by the provided key. This method provides an + * easy access to the underlying map. + * @param key A string representing the key. + * @return An object indexed by the provided key. + */ + public Object get(String key) { + return map.get(key); + } + + /** + * Puts the object in the map and indexes it in the provided key. This + * method provides an easy access to the underlying map. + * @param key A string representing the key. + * @param value The object to be indexed by the provided key. + */ + public void put(String key, Object value) { + map.put(key, value); + } + + /** + * Checks if the map contains the provided key. This is actually a wrapper + * to the private map's method of the same name. + * @param key The key to be checked. + * @return A boolean value indicating if the map contains the provided key. + */ + public boolean contains(String key) { + return map.containsKey(key); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/controller/LanguageController.java b/support/arara/source/src/main/java/com/github/cereda/arara/controller/LanguageController.java new file mode 100644 index 0000000000..a0ccc2a958 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/controller/LanguageController.java @@ -0,0 +1,120 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.controller; + +import ch.qos.cal10n.IMessageConveyor; +import ch.qos.cal10n.MessageConveyor; +import java.util.Locale; + +/** + * Implements the language controller. This controller provides a singleton + * object that holds the application messages, easily available to all model + * and utilitary classes. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class LanguageController { + + // the controller itself, since we have a singleton; + // this is the reference instance, instantiated once + private static final LanguageController instance = + new LanguageController(); + + // the message conveyor helps us to get localized messages + // according to the provided locale + private IMessageConveyor conveyor; + + /** + * Private constructor. The fallback language is set to English for all + * messages in arara. + */ + private LanguageController() { + conveyor = new MessageConveyor(new Locale("en")); + } + + /** + * Initializes the class. This method actually does nothing, it just + * triggers the static attributions. Dirty, isn't it? + */ + public static void init() { + // quack, quack, quack! + } + + /** + * Gets the singleton reference. Since this class is implemented as a + * singleton, you will get the same controller every single time. + * @return The language controller which holds the conveyor. + */ + public static LanguageController getInstance() { + return instance; + } + + /** + * 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. + */ + public void setLocale(Locale locale) { + conveyor = new 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 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. + */ + public > String getMessage(E key, Object... parameters) { + return 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 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. + */ + public > String getMessage(E key) { + return conveyor.getMessage(key); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/controller/LoggingController.java b/support/arara/source/src/main/java/com/github/cereda/arara/controller/LoggingController.java new file mode 100644 index 0000000000..193726fcc4 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/controller/LoggingController.java @@ -0,0 +1,109 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.controller; + +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.slf4j.LoggerFactory; + +/** + * Implements the logging controller. This class actually sets the logging + * configuration in order to allow appending results to a file. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class LoggingController { + + /** + * 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. + */ + public static void enableLogging(boolean enable) { + + // get the logger context from a factory, set a + // new context and reset it + LoggerContext loggerContext = + (LoggerContext) LoggerFactory.getILoggerFactory(); + + try { + + // get a new configuration and set + // the context + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(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 + String name = (String) ConfigurationController. + getInstance().get("execution.log.name"); + loggerContext.putProperty("name", name); + configurator.doConfigure(getResource()); + } + } catch (JoranException exception) { + // quack, quack, quack! + } + } + + /** + * Get the configuration resource as an input stream. The configuration + * is actually a XML file. + * @return An input stream of the provided configuration XML resource. + */ + private static InputStream getResource() { + String resource = "/com/github/cereda/arara/configuration/logback.xml"; + return LoggingController.class.getResourceAsStream(resource); + } + + /** + * Initializes the logging controller by disabling it. I don't want an odd + * behaviour out of the box. + */ + public static void init() { + enableLogging(false); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/controller/SessionController.java b/support/arara/source/src/main/java/com/github/cereda/arara/controller/SessionController.java new file mode 100644 index 0000000000..ac5a45d2b3 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/controller/SessionController.java @@ -0,0 +1,124 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.controller; + +import java.util.HashMap; +import java.util.Map; + +/** + * Implements a session controller. 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 Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class SessionController { + + // the controller itself, since we have a singleton; + // this is the reference instance, instantiated once + private static final SessionController instance = + new SessionController(); + + // 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 final Map map; + + /** + * Private constructor. Called once when the singleton is created. + */ + private SessionController() { + map = new HashMap(); + } + + /** + * Gets the singleton reference. Since this class is implemented as a + * singleton, you will get the same controller every single time. + * @return The session controller which holds the session map. + */ + public static SessionController getInstance() { + return instance; + } + + /** + * 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. + */ + public Object get(String key) { + return map.get(key); + } + + /** + * Puts the object in the session map and indexes it under the provided + * key. This method actually holds the map method of the very same name. + * @param key The provided map key. + * @param value The object to be indexed under the provided key. + */ + public void put(String key, Object value) { + map.put(key, value); + } + + /** + * Checks if the session map contains the provided key. This method holds + * the map method of the very same name. + * @param key The key to be checked. + * @return A boolean value indicating if the session map contains the + * provided key. + */ + public boolean contains(String key) { + return map.containsKey(key); + } + + /** + * Remove an entry from the map according to the provided key. This method + * holds the map method of the same name. + * @param key The provided key to indicate which session map entry should + * be removed. + */ + public void remove(String key) { + map.remove(key); + } + + /** + * Clears the session map. This method, as usual, holds the map method of + * the same name. + */ + public void clear() { + map.clear(); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/controller/SystemCallController.java b/support/arara/source/src/main/java/com/github/cereda/arara/controller/SystemCallController.java new file mode 100644 index 0000000000..9a592853f6 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/controller/SystemCallController.java @@ -0,0 +1,153 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.controller; + +import java.util.HashMap; +import java.util.Map; +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. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class SystemCallController { + + /** + * Implements a private command interface. + */ + private interface Command { + + /** + * Executes the command body. + * @return An object. + */ + public Object execute(); + } + + // the controller itself, since we have a singleton; + // this is the reference instance, instantiated once + private static final SystemCallController instance = + new SystemCallController(); + + // 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 + private final Map map; + + // 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 + private final Map commands; + + /** + * Private constructor. Called once when the singleton is created. + */ + private SystemCallController() { + + // create the new map instance to be + // populated on demand + map = new HashMap(); + + // create the new map of commands and + // add the corresponding system calls + commands = new HashMap(); + + // add the check for a Cygwin + // environment in here + commands.put("cygwin", new Command() { + + /** + * Implements the body of the command. In this particular + * instance, it checks if we are inside a Cygwin environment. + * @return A boolean value indicating if we are inside a Cygwin + * environment. + */ + public Object execute() { + try { + + // 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 + return ( + new ProcessExecutor().command("uname", "-s"). + readOutput(true).execute().outputUTF8() + ).toLowerCase().startsWith("cygwin"); + + } catch (Exception exception) { + + // gracefully fallback in case of any nasty and evil + // exception, e.g, if the command is unavailable + return false; + } + } + }); + } + + /** + * Gets the singleton reference. Since this class is implemented as a + * singleton, you will get the same controller every single time. + * @return The system call controller which holds the system call map. + */ + public static SystemCallController getInstance() { + return instance; + } + + /** + * 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. + */ + public Object get(String key) { + + // if key is not found, meaning that + // the value wasn't required before + if (!map.containsKey(key)) { + + // perform the system call and + // populate the corresponding value + map.put(key, commands.get(key).execute()); + } + + // simply return the corresponding + // value based on the provided key + return map.get(key); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/AraraException.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/AraraException.java new file mode 100644 index 0000000000..f2a340fd01 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/AraraException.java @@ -0,0 +1,88 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +/** + * Implements the specific exception model for arara. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class AraraException extends Exception { + + // the underlying exception, + // used to hold more details + // on what really happened + private Exception exception; + + /** + * Constructor. Takes the exception message. + * @param message The exception message. + */ + public AraraException(String message) { + super(message); + } + + /** + * Constructor. Takes the exception message and the underlying exception. + * @param message The exception message. + * @param exception The underlying exception object. + */ + public AraraException(String message, Exception exception) { + super(message); + this.exception = exception; + } + + /** + * Gets the underlying exception. + * @return The underlying message. + */ + public Exception getException() { + return exception; + } + + /** + * 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. + */ + public boolean hasException() { + if (exception != null) { + return (exception.getMessage() != null); + } else { + return false; + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Argument.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Argument.java new file mode 100644 index 0000000000..0b15baf9d7 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Argument.java @@ -0,0 +1,125 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.utils.CommonUtils; + +/** + * The rule argument model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Argument { + + // the argument identifier + private String identifier; + + // a boolean indicating if the + // current argument is required + private boolean required; + + // the flag to hold the argument + // value manipulation + private String flag; + + // the argument fallback if it is + // not defined in the directive + private String fallback; + + /** + * Gets the identifier. + * @return The identifier. + */ + public String getIdentifier() { + return CommonUtils.removeKeyword(identifier); + } + + /** + * Sets the identifier. + * @param identifier The identifier. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Checks if the argument is required. + * @return A boolean value indicating if the argument is required. + */ + public boolean isRequired() { + return required; + } + + /** + * Sets the argument requirement. + * @param required A boolean value. + */ + public void setRequired(boolean required) { + this.required = required; + } + + /** + * Gets the argument flag. + * @return The flag. + */ + public String getFlag() { + return CommonUtils.removeKeyword(flag); + } + + /** + * Sets the argument flag. + * @param flag The argument flag. + */ + public void setFlag(String flag) { + this.flag = flag; + } + + /** + * Gets the argument fallback. + * @return The argument fallback. + */ + public String getDefault() { + return CommonUtils.removeKeyword(fallback); + } + + /** + * Sets the argument fallback. + * @param fallback The argument fallback. + */ + public void setDefault(String fallback) { + this.fallback = fallback; + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Command.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Command.java new file mode 100644 index 0000000000..3058389832 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Command.java @@ -0,0 +1,122 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.utils.CommonUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Implements a command model, containing a list of strings. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Command { + + // a list of elements which are components + // of a command and represented as strings + private final List elements; + + // an optional file acting as a reference + // for the default working directory + private File workingDirectory; + + /** + * Constructor. + * @param values An array of objects. + */ + public Command(Object... values) { + elements = new ArrayList(); + List result = CommonUtils.flatten(Arrays.asList(values)); + for (Object value : result) { + String element = String.valueOf(value); + if (!CommonUtils.checkEmptyString(element)) { + elements.add(element); + } + } + } + + /** + * Constructor. + * @param elements A list of strings. + */ + public Command(List elements) { + this.elements = elements; + } + + /** + * Gets the list of strings representing each element of a command. + * @return A list of strings. + */ + public List getElements() { + return elements; + } + + /** + * Sets the working directory. + * @param workingDirectory A file representing the working directory. + */ + public void setWorkingDirectory(File workingDirectory) { + this.workingDirectory = workingDirectory; + } + + /** + * Gets the working directory, if any. + * @return A file representing the working directory. + */ + public File getWorkingDirectory() { + return workingDirectory; + } + + /** + * Checks if a working directory was defined. + * @return A logic value indicating if a working directory was defined. + */ + public boolean hasWorkingDirectory() { + return workingDirectory != null; + } + + /** + * Provides a textual representation of the current command. + * @return A string representing the current command. + */ + @Override + public String toString() { + return CommonUtils.getCollectionElements(elements, "[ ", " ]", ", "); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Conditional.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Conditional.java new file mode 100644 index 0000000000..4c830f0a1b --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Conditional.java @@ -0,0 +1,139 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +/** + * The conditional class, it represents the type of conditional available + * for a directive and its corresponding expression to be evaluated. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Conditional { + + // these are all types of conditionals arara + // is able to recognize; personally, I believe + // they are more than sufficient to cover the + // majority of test cases + public enum 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 oly if the result is true, and the process is + // repeated while the result still holds true + WHILE + } + + // the conditional type, specified above; the + // default fallback, as seen in the constructor, + // is set to NONE, that is, no conditional at all + private ConditionalType type; + + // the expression to be evaluated according to its + // type; the default fallback, as seen in the + // constructor, is set to an empty string + private String condition; + + /** + * Constructor. + */ + public Conditional() { + type = ConditionalType.NONE; + condition = ""; + } + + /** + * Gets the conditional type. + * @return The conditional type. + */ + public ConditionalType getType() { + return type; + } + + /** + * Sets the conditional type. + * @param type The conditional type. + */ + public void setType(ConditionalType type) { + this.type = type; + } + + /** + * Gets the condition, that is, the expression to be evaluated. + * @return A string representing the condition. + */ + public String getCondition() { + return condition; + } + + /** + * Sets the condition, that is, the expression to be evaluated. + * @param condition A string representing the condition. + */ + public void setCondition(String condition) { + this.condition = condition; + } + + /** + * Provides a textual representation of the conditional object. + * @return A string representation of this object. + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{ ").append(type); + if (type != ConditionalType.NONE) { + builder.append(", expression: ").append(condition.trim()); + } + builder.append(" }"); + return builder.toString(); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Configuration.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Configuration.java new file mode 100644 index 0000000000..0c2c294958 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Configuration.java @@ -0,0 +1,246 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.utils.CommonUtils; +import com.github.cereda.arara.utils.ConfigurationUtils; +import java.io.File; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Implements the configuration model, which holds the default settings and can + * load the configuration file. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Configuration { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Loads the application configuration. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static void load() throws AraraException { + + // initialize both file type and language models, + // since we can track errors from there instead + // of relying on a check on this level + FileType.init(); + Language.init(); + + // reset everything + reset(); + + // get the configuration file, if any + File file = ConfigurationUtils.getConfigFile(); + if (file != null) { + + // set the configuration file name for + // logging purposes + ConfigurationController.getInstance(). + put("execution.configuration.name", + CommonUtils.getCanonicalPath(file) + ); + + // then validate it and update the + // configuration accordingly + Resource resource = ConfigurationUtils.validateConfiguration(file); + update(resource); + } + + // just to be sure, update the + // current locale in order to + // display localized messages + Locale locale = ((Language) ConfigurationController. + getInstance().get("execution.language")).getLocale(); + LanguageController.getInstance().setLocale(locale); + } + + /** + * Resets the configuration to initial settings. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + private static void reset() throws AraraException { + + // put everything in a map to be + // later assigned to the configuration + // controller, which holds the settings + Map mapping = new HashMap(); + + mapping.put("execution.loops", 10L); + mapping.put("directives.charset", Charset.forName("UTF-8")); + mapping.put("execution.errors.halt", true); + mapping.put("execution.timeout", false); + mapping.put("execution.timeout.value", 0L); + mapping.put("execution.timeout.unit", TimeUnit.MILLISECONDS); + mapping.put("application.version", "4.0"); + mapping.put("application.revision", "1"); + mapping.put("directives.linebreak.pattern", "^\\s*-->\\s(.*)$"); + + String directive = "^\\s*(\\w+)\\s*(:\\s*(\\{.*\\})\\s*)?"; + String pattern = "(\\s+(if|while|until|unless)\\s+(\\S.*))?$"; + + mapping.put("directives.pattern", directive.concat(pattern)); + mapping.put("application.pattern", "arara:\\s"); + mapping.put("application.width", 65); + mapping.put("execution.database.name", "arara"); + mapping.put("execution.log.name", "arara"); + mapping.put("execution.verbose", false); + + mapping.put("trigger.halt", false); + mapping.put("execution.language", new Language("en")); + mapping.put("execution.logging", false); + mapping.put("execution.dryrun", false); + mapping.put("execution.status", 0); + mapping.put("application.copyright.year", "2012-2018"); + mapping.put("execution.filetypes", ConfigurationUtils. + getDefaultFileTypes()); + mapping.put("execution.rule.paths", + Arrays.asList( + CommonUtils.buildPath( + ConfigurationUtils.getApplicationPath(), + "rules" + ) + ) + ); + + mapping.put("execution.preambles", new HashMap()); + mapping.put("execution.preamble.active", false); + mapping.put("execution.configuration.name", "[none]"); + mapping.put("execution.header", false); + mapping.put("ui.lookandfeel", "none"); + + // get the configuration controller and + // set every map key to it + ConfigurationController controller = + ConfigurationController.getInstance(); + for (String key : mapping.keySet()) { + controller.put(key, mapping.get(key)); + } + } + + /** + * Update the configuration based on the provided map. + * @param data Map containing the new configuration settings. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + private static void update(Resource resource) throws AraraException { + + ConfigurationController controller = + ConfigurationController.getInstance(); + + if (resource.getPaths() != null) { + List paths = resource.getPaths(); + paths = ConfigurationUtils.normalizePaths(paths); + controller.put("execution.rule.paths", paths); + } + + if (resource.getFiletypes() != null) { + List resources = resource.getFiletypes(); + List filetypes = new ArrayList(); + for (FileTypeResource type : resources) { + if (type.getPattern() != null) { + filetypes.add( + new FileType(type.getExtension(), type.getPattern()) + ); + } else { + filetypes.add(new FileType(type.getExtension())); + } + } + filetypes = ConfigurationUtils.normalizeFileTypes(filetypes); + controller.put("execution.filetypes", filetypes); + } + + controller.put("execution.verbose", resource.isVerbose()); + controller.put("execution.logging", resource.isLogging()); + controller.put("execution.header", resource.isHeader()); + + if (resource.getDbname() != null) { + controller.put("execution.database.name", + ConfigurationUtils.cleanFileName(resource.getDbname())); + } + + if (resource.getLogname() != null) { + controller.put("execution.log.name", + ConfigurationUtils.cleanFileName(resource.getLogname())); + } + + if (resource.getLanguage() != null) { + controller.put("execution.language", + new Language(resource.getLanguage())); + } + + long loops = resource.getLoops(); + if (loops > 0) { + controller.put("execution.loops", loops); + } else { + if (loops < 0) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CONFIGURATION_LOOPS_INVALID_RANGE + ) + ); + } + } + + if (resource.getPreambles() != null) { + controller.put("execution.preambles", + resource.getPreambles()); + } + + if (resource.getLaf() != null) { + controller.put("ui.lookandfeel", + resource.getLaf()); + } + + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Database.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Database.java new file mode 100644 index 0000000000..66d360fdc0 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Database.java @@ -0,0 +1,80 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import java.util.HashMap; +import org.simpleframework.xml.ElementMap; +import org.simpleframework.xml.Root; + +/** + * The XML database model, which keeps track on file changes. I am using the + * Simple framework to marshall and unmarshall objects and XML files. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +@Root(name = "status") +public 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; the XML map is done + // inline, so it does not clutter the output a lot + @ElementMap(entry = "hash", key = "file", attribute = true, inline = true) + private HashMap map; + + /** + * Constructor. It creates a new map. + */ + public Database() { + map = new HashMap(); + } + + /** + * Gets the map. + * @return The map. + */ + public HashMap getMap() { + return map; + } + + /** + * Sets the map. + * @param map The map. + */ + public void setMap(HashMap map) { + this.map = map; + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Directive.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Directive.java new file mode 100644 index 0000000000..e311477125 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Directive.java @@ -0,0 +1,146 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import java.util.List; +import java.util.Map; + +/** + * Implements the directive model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Directive { + + // the directive identifier, it is resolved + // to the rule identifier later on + private String identifier; + + // a map containing the parameters; they + // are validated later on in order to + // ensure they are valid + private Map parameters; + + // a conditional containing the type and + // the expression to be evaluated later on + private Conditional conditional; + + // a list contained all line numbers from + // the main file which built the current + // directive + private List lineNumbers; + + /** + * Gets the directive identifier. + * @return A string representing the directive identifier. + */ + public String getIdentifier() { + return identifier; + } + + /** + * Sets the directive identifier. + * @param identifier A string representing the directive identifier. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Gets the directive parameters. + * @return A map containing the directive parameters. + */ + public Map getParameters() { + return parameters; + } + + /** + * Sets the directive parameters. + * @param parameters A map containing the directive parameters. + */ + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + /** + * Gets the conditional object from the current directive. + * @return The conditional object from the current directive. + */ + public Conditional getConditional() { + return conditional; + } + + /** + * Sets the conditional object from the current directive. + * @param conditional The conditional object from the current directive. + */ + public void setConditional(Conditional conditional) { + this.conditional = conditional; + } + + /** + * Gets the list containing all line numbers from the current directive. + * @return A list containing all line numbers from the current directive. + */ + public List getLineNumbers() { + return lineNumbers; + } + + /** + * Sets the list containing all line numbers from the current directive. + * @param lineNumbers A list containing all line numbers from the current + * directive. + */ + public void setLineNumbers(List lineNumbers) { + this.lineNumbers = lineNumbers; + } + + /** + * Provides a textual representation of the current directive. + * @return A string containing a textual representation of the current + * directive. + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Directive: { "); + builder.append("identifier: ").append(identifier).append(", "); + builder.append("parameters: ").append(parameters).append(", "); + builder.append("conditional: ").append(conditional).append(", "); + builder.append("lines: ").append(lineNumbers).append(" }"); + return builder.toString(); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Evaluator.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Evaluator.java new file mode 100644 index 0000000000..41f8d09943 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Evaluator.java @@ -0,0 +1,158 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.utils.CommonUtils; +import com.github.cereda.arara.utils.Methods; +import java.util.HashMap; +import java.util.Map; +import org.mvel2.templates.TemplateRuntime; + +/** + * Implements the evaluator model, on which a conditional can be analyzed and + * processed. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Evaluator { + + // this attribute holds the maximum number of + // loops arara will accept; it's like + // reaching infinity + private final long loops; + + // 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 long counter; + + // a flag that indicates the + // evaluation to halt regardless + // of the the result + private boolean halt; + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Constructor. It gets the application maximum number of loops and reset + * all counters. + */ + public Evaluator() { + loops = (Long) ConfigurationController.getInstance(). + get("execution.loops"); + counter = 0; + halt = false; + } + + /** + * 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. + */ + public boolean evaluate(Conditional conditional) throws AraraException { + + // when in dry-run mode, arara + // always ignore conditional evaluations + if (((Boolean) ConfigurationController. + getInstance().get("execution.dryrun")) == true) { + return false; + } + + switch (conditional.getType()) { + case NONE: + return false; + case IF: + case UNLESS: + if (!halt) { + halt = true; + } else { + return false; + } + break; + } + + // check counters and see if the execution + // has reached our concept of infinity, + // thus breaking the cycles + counter++; + if (((conditional.getType() == Conditional.ConditionalType.WHILE) && + (counter > loops)) || + ((conditional.getType() == Conditional.ConditionalType.UNTIL) && + (counter >= loops))) { + return false; + } else { + + Map context = new HashMap(); + Methods.addConditionalMethods(context); + + try { + Object result = TemplateRuntime.eval("@{ ".concat( + conditional.getCondition()).concat(" }"), context); + if (!CommonUtils.checkClass(Boolean.class, result)) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_EVALUATE_NOT_BOOLEAN_VALUE + ) + ); + } else { + boolean value = (Boolean) result; + switch (conditional.getType()) { + case UNLESS: + case UNTIL: + value = !value; + break; + } + return value; + } + } catch (RuntimeException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_EVALUATE_COMPILATION_FAILED + ), + exception + ); + } + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Extractor.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Extractor.java new file mode 100644 index 0000000000..84edef1eb0 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Extractor.java @@ -0,0 +1,88 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.utils.CommonUtils; +import com.github.cereda.arara.utils.DirectiveUtils; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import org.apache.commons.io.FileUtils; + +/** + * It extracts directives from the provided main file. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Extractor { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Extracts a list of directives from the provided main file, obtained from + * the configuration controller. + * @return A list of directives. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public List extract() throws AraraException { + + File file = (File) ConfigurationController. + getInstance().get("execution.reference"); + Charset charset = (Charset) ConfigurationController. + getInstance().get("directives.charset"); + + try { + List content = CommonUtils.getPreambleContent(); + List lines = FileUtils.readLines(file, charset.name()); + content.addAll(lines); + return DirectiveUtils.extractDirectives(content); + } catch (IOException ioexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_EXTRACTOR_IO_ERROR + ), + ioexception + ); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/FileType.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/FileType.java new file mode 100644 index 0000000000..b9f2a01e03 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/FileType.java @@ -0,0 +1,170 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.utils.CommonUtils; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +/** + * Implements the file type model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class FileType { + + // string representing the + // file extension + private String extension; + + // string representing the + // file pattern to be used + // as directive lookup + private String pattern; + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + // a map containing all file + // types that arara accepts + private static final Map types = + new HashMap(); + + /** + * Initializes the file type class by setting the default file types and + * their corresponding patterns. + */ + public static void init() { + types.put("tex", "^\\s*%\\s+"); + types.put("dtx", "^\\s*%\\s+"); + types.put("ltx", "^\\s*%\\s+"); + types.put("drv", "^\\s*%\\s+"); + types.put("ins", "^\\s*%\\s+"); + } + + /** + * Constructor. It takes both file extension and pattern lookup. + * @param extension The file extension. + * @param pattern The file pattern. + */ + public FileType(String extension, String pattern) { + this.extension = extension; + this.pattern = pattern; + } + + /** + * Constructor. It takes the extension, but it might raise an exception if + * the extension is unknown. This constructor is used when you just want + * to reorganize the file lookup priority without the need of changing the + * default extensions. + * @param extension The file extension. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public FileType(String extension) throws AraraException { + if (types.containsKey(extension)) { + this.extension = extension; + this.pattern = types.get(extension); + } else { + throw new AraraException( + messages.getMessage( + Messages.ERROR_FILETYPE_UNKNOWN_EXTENSION, + extension, + CommonUtils.getFileTypesList() + ) + ); + } + } + + /** + * Implements the file type hash code. Note that only the file extension is + * considered. + * @return An integer representing the file type hash code. + */ + @Override + public int hashCode() { + return new HashCodeBuilder().append(extension).toHashCode(); + } + + /** + * Implements the file type equals method, checking if one file type is + * equal to another. Note that only the file extension is considered. + * @param object The object to be analyzed. + * @return A boolean value indicating if those two objects are equal. + */ + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } + if (getClass() != object.getClass()) { + return false; + } + final FileType reference = (FileType) object; + return new EqualsBuilder().append(extension, reference.extension).isEquals(); + } + + /** + * Gets the file type extension. + * @return String representing the file type extension. + */ + public String getExtension() { + return extension; + } + + /** + * Gets the file type pattern. + * @return String representing the file type pattern. + */ + public String getPattern() { + return 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 + public String toString() { + return ".".concat(extension); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/FileTypeResource.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/FileTypeResource.java new file mode 100644 index 0000000000..2934035858 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/FileTypeResource.java @@ -0,0 +1,84 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.utils.CommonUtils; + +/** + * Implements the file type resource model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class FileTypeResource { + + // the file extension + private String extension; + + // the file pattern + private String pattern; + + /** + * Gets the extension. + * @return The extension. + */ + public String getExtension() { + return CommonUtils.removeKeyword(extension); + } + + /** + * Sets the extension. + * @param extension The extension. + */ + public void setExtension(String extension) { + this.extension = extension; + } + + /** + * Gets the pattern. + * @return The pattern. + */ + public String getPattern() { + return CommonUtils.removeKeyword(pattern); + } + + /** + * Sets the pattern. + * @param pattern The pattern. + */ + public void setPattern(String pattern) { + this.pattern = pattern; + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Interpreter.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Interpreter.java new file mode 100644 index 0000000000..7c8f6e3928 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Interpreter.java @@ -0,0 +1,458 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.utils.CommonUtils; +import com.github.cereda.arara.utils.DisplayUtils; +import com.github.cereda.arara.utils.InterpreterUtils; +import com.github.cereda.arara.utils.Methods; +import com.github.cereda.arara.utils.RuleUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.mvel2.templates.TemplateRuntime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Interprets the list of directives. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Interpreter { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + // the class logger obtained from + // the logger factory + private static final Logger logger = + LoggerFactory.getLogger(Interpreter.class); + + // list of directives to be + // interpreted in here + private List directives; + + /** + * Sets the list of directives. + * @param directives The list of directives. + */ + public void setDirectives(List directives) { + this.directives = directives; + } + + /** + * Executes each directive, throwing an exception if something bad has + * happened. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public void execute() throws AraraException { + + for (Directive directive : directives) { + + logger.info( + messages.getMessage( + Messages.LOG_INFO_INTERPRET_RULE, + directive.getIdentifier() + ) + ); + + ConfigurationController.getInstance(). + put("execution.file", + directive.getParameters().get("reference") + ); + File file = getRule(directive); + + logger.info( + messages.getMessage( + Messages.LOG_INFO_RULE_LOCATION, + file.getParent() + ) + ); + + ConfigurationController.getInstance(). + put("execution.info.rule.id", directive.getIdentifier()); + ConfigurationController.getInstance(). + put("execution.info.rule.path", file.getParent()); + ConfigurationController.getInstance(). + put("execution.directive.lines", + directive.getLineNumbers() + ); + ConfigurationController.getInstance(). + put("execution.directive.reference", + directive.getParameters().get("reference") + ); + + Rule rule = parseRule(file, directive); + Map parameters = parseArguments(rule, directive); + Methods.addRuleMethods(parameters); + + String name = rule.getName(); + List authors = rule.getAuthors() == null ? + new ArrayList() : rule.getAuthors(); + ConfigurationController.getInstance(). + put("execution.rule.arguments", + InterpreterUtils.getRuleArguments(rule) + ); + + Evaluator evaluator = new Evaluator(); + + boolean available = true; + if (InterpreterUtils. + runPriorEvaluation(directive.getConditional())) { + available = evaluator.evaluate(directive.getConditional()); + } + + if (available) { + + do { + + List commands = rule.getCommands(); + for (RuleCommand command : commands) { + String closure = command.getCommand(); + Object result = null; + try { + result = TemplateRuntime.eval(closure, parameters); + } catch (RuntimeException exception) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_INTERPRETER_COMMAND_RUNTIME_ERROR + ) + ), + exception + ); + } + + List execution = new ArrayList(); + if (CommonUtils.checkClass(List.class, result)) { + execution = CommonUtils.flatten((List) result); + } else { + execution.add(result); + } + + for (Object current : execution) { + + if (current == null) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_INTERPRETER_NULL_COMMAND + ) + ) + ); + } else { + + if (!CommonUtils.checkEmptyString( + String.valueOf(current))) { + + DisplayUtils.printEntry(name, + command.getName() == null ? + messages.getMessage( + Messages.INFO_LABEL_UNNAMED_TASK + ) + : command.getName() + ); + boolean success = true; + + if (CommonUtils.checkClass( + Trigger.class, current)) { + if (((Boolean) ConfigurationController. + getInstance(). + get("execution.dryrun")) == false) { + if (((Boolean) ConfigurationController. + getInstance(). + get("execution.verbose")) == true) { + DisplayUtils.wrapText( + messages.getMessage( + Messages.INFO_INTERPRETER_VERBOSE_MODE_TRIGGER_MODE + ) + ); + } + } else { + DisplayUtils.printAuthors(authors); + DisplayUtils.wrapText( + messages.getMessage( + Messages.INFO_INTERPRETER_DRYRUN_MODE_TRIGGER_MODE + ) + ); + DisplayUtils.printConditional( + directive.getConditional() + ); + } + Trigger trigger = (Trigger) current; + trigger.process(); + } else { + Object representation = + CommonUtils.checkClass( + Command.class, + current + ) ? + current + : String.valueOf(current); + logger.info( + messages.getMessage( + Messages.LOG_INFO_SYSTEM_COMMAND, + representation + ) + ); + + if (((Boolean) ConfigurationController. + getInstance(). + get("execution.dryrun")) == false) { + int code = InterpreterUtils. + run(representation); + Object check = null; + try { + Map context = + new HashMap(); + context.put("value", code); + check = TemplateRuntime.eval( + "@{ ".concat( + command.getExit() == null ? + "value == 0" + : command.getExit()).concat(" }"), + context); + } catch (RuntimeException exception) { + throw new AraraException( + CommonUtils.getRuleErrorHeader(). + concat( + messages.getMessage( + Messages.ERROR_INTERPRETER_EXIT_RUNTIME_ERROR + ) + ), + exception + ); + } + if (CommonUtils. + checkClass( + Boolean.class, + check)) { + success = (Boolean) check; + } else { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_INTERPRETER_WRONG_EXIT_CLOSURE_RETURN + ) + ) + ); + } + } else { + DisplayUtils.printAuthors(authors); + DisplayUtils.wrapText( + messages.getMessage( + Messages.INFO_INTERPRETER_DRYRUN_MODE_SYSTEM_COMMAND, + representation + ) + ); + DisplayUtils.printConditional( + directive.getConditional() + ); + } + } + + DisplayUtils.printEntryResult(success); + + if (((Boolean) ConfigurationController. + getInstance().get("trigger.halt")) + || (((Boolean) ConfigurationController. + getInstance(). + get("execution.errors.halt") + && !success))) { + return; + } + } + } + } + } + } while (evaluator.evaluate(directive.getConditional())); + } + } + } + + /** + * 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. + */ + private File getRule(Directive directive) throws AraraException { + File file = InterpreterUtils.buildRulePath(directive.getIdentifier()); + if (file == null) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_INTERPRETER_RULE_NOT_FOUND, + directive.getIdentifier(), + CommonUtils.getCollectionElements( + CommonUtils.getAllRulePaths(), + "(", + ")", + "; " + ) + ) + ); + } else { + return file; + } + } + + /** + * Parses the rule against the provided directive. + * @param file The file representing the rule. + * @param directive The directive to be analyzed. + * @return A rule object. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + private Rule parseRule(File file, Directive directive) + throws AraraException { + return RuleUtils.parseRule(file, directive.getIdentifier()); + } + + /** + * 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. + */ + private Map parseArguments(Rule rule, Directive directive) + throws AraraException { + + List arguments = rule.getArguments(); + + Set unknown = CommonUtils. + getUnknownKeys(directive.getParameters(), arguments); + unknown.remove("file"); + unknown.remove("reference"); + if (!unknown.isEmpty()) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_INTERPRETER_UNKNOWN_KEYS, + CommonUtils.getCollectionElements( + unknown, + "(", + ")", + ", " + ) + ) + ) + ); + } + + Map mapping = new HashMap(); + mapping.put("file", directive.getParameters().get("file")); + mapping.put("reference", directive.getParameters().get("reference")); + + Map context = new HashMap(); + context.put("parameters", directive.getParameters()); + context.put("file", directive.getParameters().get("file")); + context.put("reference", directive.getParameters().get("reference")); + Methods.addRuleMethods(context); + + for (Argument argument : arguments) { + if ((argument.isRequired()) && + (!directive.getParameters().containsKey( + argument.getIdentifier()))) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_INTERPRETER_ARGUMENT_IS_REQUIRED, + argument.getIdentifier() + ) + ) + ); + } + + if (argument.getDefault() != null) { + try { + Object result = TemplateRuntime. + eval(argument.getDefault(), context); + mapping.put(argument.getIdentifier(), result); + } catch (RuntimeException exception) { + throw new AraraException( + CommonUtils.getRuleErrorHeader(). + concat(messages.getMessage( + Messages.ERROR_INTERPRETER_DEFAULT_VALUE_RUNTIME_ERROR + ) + ), + exception + ); + } + } else { + mapping.put(argument.getIdentifier(), ""); + } + + if ((argument.getFlag() != null) && + (directive.getParameters().containsKey( + argument.getIdentifier()))) { + + try { + Object result = TemplateRuntime.eval( + argument.getFlag(), + context + ); + mapping.put(argument.getIdentifier(), result); + } catch (RuntimeException exception) { + throw new AraraException(CommonUtils.getRuleErrorHeader(). + concat( + messages.getMessage( + Messages.ERROR_INTERPRETER_FLAG_RUNTIME_EXCEPTION + ) + ), + exception + ); + } + } + } + + return mapping; + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Language.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Language.java new file mode 100644 index 0000000000..2b0fc8266e --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Language.java @@ -0,0 +1,148 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.utils.CommonUtils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * Implements the language model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Language { + + // the language code, based on + // ISO 639-1 and language variants + private final String code; + + // map containing all languages + // supported by nightingale + private static final Map> languages = + new HashMap>(); + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Initialize the language model. All supported languages are added in here. + */ + public static void init() { + languages.put("en", new Pair( + "English", + new Locale("en") + )); + languages.put("de", new Pair( + "German", + new Locale("de") + )); + languages.put("nl", new Pair( + "Dutch", + new Locale("nl") + )); + languages.put("qn", new Pair( + "Broad Norfolk", + new Locale("en", "QN") + )); + languages.put("ptbr", new Pair( + "Brazilian Portuguese", + new Locale("pt", "BR") + )); + languages.put("it", new Pair( + "Italian", + new Locale("it") + )); + } + + /** + * Creates a new language object. It might raise an exception if the + * provided language does not exist in the map. + * @param code The language code, based on ISO 639-1 and language variants. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public Language(String code) throws AraraException { + if (languages.containsKey(code)) { + this.code = code; + } else { + throw new AraraException( + messages.getMessage( + Messages.ERROR_LANGUAGE_INVALID_CODE, + getLanguagesList() + ) + ); + } + } + + /** + * Gets the language name. + * @return A string representing the language name. + */ + public String getName() { + return languages.get(code).getFirstElement(); + } + + /** + * Gets the language locale. + * @return The language locale. + */ + public Locale getLocale() { + return languages.get(code).getSecondElement(); + } + + /** + * Gets a string representing the list of available languages. + * @return String representing the list of available languages. + */ + public static String getLanguagesList() { + List entries = new ArrayList(); + for (String key : languages.keySet()) { + entries.add(languages.get(key). + getFirstElement(). + concat(": "). + concat(key) + ); + } + return CommonUtils.getCollectionElements(entries, "(", ")", ", "); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Messages.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Messages.java new file mode 100644 index 0000000000..9916c3c145 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Messages.java @@ -0,0 +1,170 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import ch.qos.cal10n.BaseName; +import ch.qos.cal10n.Locale; +import ch.qos.cal10n.LocaleData; + +/** + * This enumeration contains all application messages. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +@BaseName("com.github.cereda.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") +}) +public enum 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_INVALID_YAML, + 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_GETCANONICALPATH_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_NULL_COMMAND, + ERROR_INTERPRETER_RULE_NOT_FOUND, + ERROR_INTERPRETER_UNKNOWN_KEYS, + ERROR_INTERPRETER_WRONG_EXIT_CLOSURE_RETURN, + ERROR_LANGUAGE_INVALID_CODE, + ERROR_LOAD_COULD_NOT_LOAD_XML, + ERROR_PARSER_INVALID_PREAMBLE, + ERROR_PARSER_LOOPS_INVALID_RANGE, + ERROR_PARSER_LOOPS_NAN, + ERROR_PARSER_TIMEOUT_INVALID_RANGE, + ERROR_PARSER_TIMEOUT_NAN, + ERROR_PARSERULE_GENERIC_ERROR, + ERROR_PARSERULE_INVALID_YAML, + 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_TRIGGER_ACTION_NOT_FOUND, + ERROR_TRIGGER_CALL_EXCEPTION, + ERROR_VALIDATE_EMPTY_FILES_LIST, + ERROR_VALIDATE_FILE_IS_RESERVED, + 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_ARGUMENTS_LIST, + ERROR_VALIDATEBODY_DUPLICATE_ARGUMENT_IDENTIFIERS, + ERROR_VALIDATEBODY_MISSING_KEYS, + ERROR_VALIDATEBODY_NULL_ARGUMENT_ID, + ERROR_VALIDATEBODY_NULL_COMMAND, + ERROR_VALIDATEBODY_NULL_COMMANDS_LIST, + ERROR_VALIDATEHEADER_NULL_ID, + ERROR_VALIDATEHEADER_NULL_NAME, + ERROR_VALIDATEHEADER_WRONG_IDENTIFIER, + ERROR_VELOCITY_FILE_NOT_FOUND, + ERROR_VELOCITY_PARSE_EXCEPTION, + ERROR_VELOCITY_METHOD_INVOCATION_EXCEPTION, + ERROR_VELOCITY_IO_EXCEPTION, + INFO_DISPLAY_EXCEPTION_MORE_DETAILS, + INFO_DISPLAY_EXECUTION_TIME, + INFO_DISPLAY_FILE_INFORMATION, + INFO_INTERPRETER_DRYRUN_MODE_SYSTEM_COMMAND, + INFO_INTERPRETER_DRYRUN_MODE_TRIGGER_MODE, + INFO_INTERPRETER_VERBOSE_MODE_TRIGGER_MODE, + 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_ALL_RIGHTS_RESERVED, + INFO_PARSER_DRYRUN_MODE_DESCRIPTION, + INFO_PARSER_HELP_DESCRIPTION, + INFO_PARSER_LANGUAGE_DESCRIPTION, + INFO_PARSER_LOG_DESCRIPTION, + INFO_PARSER_LOOPS_DESCRIPTION, + INFO_PARSER_NOTES, + INFO_PARSER_ONLY_HEADER, + INFO_PARSER_PREAMBLE_DESCRIPTION, + INFO_PARSER_SILENT_MODE_DESCRIPTION, + INFO_PARSER_TIMEOUT_DESCRIPTION, + INFO_PARSER_VERBOSE_MODE_DESCRIPTION, + INFO_PARSER_VERSION_DESCRIPTION, + LOG_INFO_BEGIN_BUFFER, + 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, + ERROR_CONFIGURATION_FILETYPE_MISSING_EXTENSION + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Pair.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Pair.java new file mode 100644 index 0000000000..d8591b6ef7 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Pair.java @@ -0,0 +1,92 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +/** + * Implements a pair of objects. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Pair { + + // first element of the pair + private final T firstElement; + + // second element of the pair + private final V secondElement; + + /** + * Constructor. + * @param firstElement The first element. + * @param secondElement The second element. + */ + public Pair(T firstElement, V secondElement) { + this.firstElement = firstElement; + this.secondElement = secondElement; + } + + /** + * Gets the first element. + * @return The first element. + */ + public T getFirstElement() { + return firstElement; + } + + /** + * Gets the second element. + * @return The second element. + */ + public V getSecondElement() { + return secondElement; + } + + /** + * A shorter version for getting the first element. + * @return The first element. + */ + public T first() { + return getFirstElement(); + } + + /** + * A shorter version for getting the second element. + * @return The second element. + */ + public V second() { + return getSecondElement(); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Parser.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Parser.java new file mode 100644 index 0000000000..723e24e76e --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Parser.java @@ -0,0 +1,410 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.controller.LoggingController; +import com.github.cereda.arara.utils.CommonUtils; +import com.github.cereda.arara.utils.DisplayUtils; +import java.util.Locale; +import java.util.Map; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** + * Implements the command line parser. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Parser { + + // command line arguments to be + // processed by this parser + private final String[] arguments; + + // command line options, it will + // group each option available + // in arara + private Options options; + + // each option available in + // arara + private Option version; + private Option help; + private Option log; + private Option verbose; + private Option silent; + private Option dryrun; + private Option timeout; + private Option language; + private Option loops; + private Option preamble; + private Option onlyheader; + + public Parser() { + this.arguments = null; + } + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Constructor. + * @param arguments Array of strings representing the command line + * arguments. + */ + public Parser(String[] arguments) { + this.arguments = arguments; + } + + /** + * Parses the command line arguments. + * @return A boolean value indicating if the parsing should allow the + * application to look for directives in the provided main file. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public boolean parse() throws AraraException { + + // create new instances of the + // command line options, including + // the ones that require arguments + version = new Option("V", "version", false, ""); + help = new Option("h", "help", false, ""); + log = new Option("l", "log", false, ""); + verbose = new Option("v", "verbose", false, ""); + silent = new Option("s", "silent", false, ""); + dryrun = new Option("n", "dry-run", false, ""); + timeout = new Option("t", "timeout", true, ""); + timeout.setArgName("number"); + language = new Option("L", "language", true, ""); + language.setArgName("code"); + loops = new Option("m", "max-loops", true, ""); + loops.setArgName("number"); + preamble = new Option("p", "preamble", true, ""); + preamble.setArgName("name"); + onlyheader = new Option("H", "header", false, ""); + + // add all options to the options + // group, so they are recognized + // by the command line parser + options = new Options(); + options.addOption(version); + options.addOption(help); + options.addOption(log); + options.addOption(verbose); + options.addOption(silent); + options.addOption(dryrun); + options.addOption(timeout); + options.addOption(language); + options.addOption(loops); + options.addOption(preamble); + options.addOption(onlyheader); + + // update all descriptions based + // on the localized messages + updateDescriptions(); + + // a new default command line + // parser is created and the + // arguments are parsed + CommandLineParser parser = new DefaultParser(); + + try { + + CommandLine line = parser.parse(options, arguments); + + String reference; + if (line.hasOption("language")) { + ConfigurationController.getInstance(). + put("execution.language", + new Language(line.getOptionValue("language")) + ); + Locale locale = ((Language) ConfigurationController. + getInstance().get("execution.language")).getLocale(); + messages.setLocale(locale); + updateDescriptions(); + } + + if (line.hasOption("help")) { + printVersion(); + printUsage(); + return false; + } + + if (line.hasOption("version")) { + printVersion(); + printNotes(); + return false; + } + + if (line.getArgs().length != 1) { + printVersion(); + printUsage(); + return false; + } else { + reference = line.getArgs()[0]; + } + + if (line.hasOption("timeout")) { + try { + long value = Long.parseLong(line.getOptionValue("timeout")); + if (value <= 0) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_PARSER_TIMEOUT_INVALID_RANGE + ) + ); + } else { + ConfigurationController.getInstance(). + put("execution.timeout", true); + ConfigurationController.getInstance(). + put("execution.timeout.value", value); + } + } catch (NumberFormatException nfexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_PARSER_TIMEOUT_NAN + ) + ); + } + } + + if (line.hasOption("max-loops")) { + try { + long value = Long.parseLong( + line.getOptionValue("max-loops") + ); + if (value <= 0) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_PARSER_LOOPS_INVALID_RANGE + ) + ); + } else { + ConfigurationController.getInstance(). + put("execution.loops", value); + } + } catch (NumberFormatException nfexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_PARSER_LOOPS_NAN + ) + ); + } + } + + if (line.hasOption("verbose")) { + ConfigurationController.getInstance(). + put("execution.verbose", true); + } + + if (line.hasOption("silent")) { + ConfigurationController.getInstance(). + put("execution.verbose", false); + } + + if (line.hasOption("dry-run")) { + ConfigurationController.getInstance(). + put("execution.dryrun", true); + ConfigurationController.getInstance(). + put("execution.errors.halt", false); + } + + if (line.hasOption("log")) { + ConfigurationController.getInstance(). + put("execution.logging", true); + } + + if (line.hasOption("preamble")) { + @SuppressWarnings("unchecked") + Map preambles = (Map) + ConfigurationController.getInstance(). + get("execution.preambles"); + if (preambles.containsKey(line.getOptionValue("preamble"))) { + ConfigurationController.getInstance(). + put("execution.preamble.active", true); + ConfigurationController.getInstance(). + put("execution.preamble.content", + preambles.get(line.getOptionValue("preamble")) + ); + } + else { + throw new AraraException( + messages.getMessage( + Messages.ERROR_PARSER_INVALID_PREAMBLE, + line.getOptionValue("preamble") + ) + ); + } + } + + if (line.hasOption("header")) { + ConfigurationController.getInstance(). + put("execution.header", true); + } + + CommonUtils.discoverFile(reference); + LoggingController.enableLogging((Boolean) ConfigurationController. + getInstance().get("execution.logging")); + ConfigurationController.getInstance().put("display.time", true); + + return true; + + } catch (ParseException pexception) { + printVersion(); + printUsage(); + return false; + } + } + + /** + * Prints the application usage. + */ + private void printUsage() { + HelpFormatter formatter = new HelpFormatter(); + StringBuilder builder = new StringBuilder(); + builder.append("arara [file [--dry-run] [--log] "); + builder.append("[--verbose | --silent] [--timeout N] "); + builder.append("[--max-loops N] [--language L] "); + builder.append("[ --preamble P ] [--header] | --help | --version]"); + formatter.printHelp(builder.toString(), options); + } + + /** + * Prints the application version. + */ + private void printVersion() { + String year = (String) ConfigurationController.getInstance(). + get("application.copyright.year"); + String number = (String) ConfigurationController.getInstance(). + get("application.version"); + String revision = (String) ConfigurationController.getInstance(). + get("application.revision"); + StringBuilder builder = new StringBuilder(); + builder.append("arara "); + builder.append(number); + builder.append(" (revision "); + builder.append(revision); + builder.append(")"); + builder.append("\n"); + builder.append("Copyright (c) ").append(year).append(", "); + builder.append("Paulo Roberto Massa Cereda"); + builder.append("\n"); + builder.append(messages.getMessage( + Messages.INFO_PARSER_ALL_RIGHTS_RESERVED) + ); + builder.append("\n"); + System.out.println(builder.toString()); + } + + /** + * Print the application notes. + */ + private void printNotes() { + DisplayUtils.wrapText(messages.getMessage(Messages.INFO_PARSER_NOTES)); + } + + /** + * Updates all the descriptions in order to make them reflect the current + * language setting. + */ + private void updateDescriptions() { + version.setDescription( + messages.getMessage( + Messages.INFO_PARSER_VERSION_DESCRIPTION + ) + ); + help.setDescription( + messages.getMessage( + Messages.INFO_PARSER_HELP_DESCRIPTION + ) + ); + log.setDescription( + messages.getMessage( + Messages.INFO_PARSER_LOG_DESCRIPTION + ) + ); + verbose.setDescription( + messages.getMessage( + Messages.INFO_PARSER_VERBOSE_MODE_DESCRIPTION + ) + ); + silent.setDescription( + messages.getMessage( + Messages.INFO_PARSER_SILENT_MODE_DESCRIPTION + ) + ); + dryrun.setDescription( + messages.getMessage( + Messages.INFO_PARSER_DRYRUN_MODE_DESCRIPTION + ) + ); + timeout.setDescription( + messages.getMessage( + Messages.INFO_PARSER_TIMEOUT_DESCRIPTION + ) + ); + language.setDescription( + messages.getMessage( + Messages.INFO_PARSER_LANGUAGE_DESCRIPTION + ) + ); + loops.setDescription( + messages.getMessage( + Messages.INFO_PARSER_LOOPS_DESCRIPTION + ) + ); + preamble.setDescription( + messages.getMessage( + Messages.INFO_PARSER_PREAMBLE_DESCRIPTION + ) + ); + onlyheader.setDescription( + messages.getMessage( + Messages.INFO_PARSER_ONLY_HEADER + ) + ); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Resource.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Resource.java new file mode 100644 index 0000000000..2994a4d691 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Resource.java @@ -0,0 +1,291 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.utils.CommonUtils; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; +import org.apache.commons.lang.SystemUtils; +import org.mvel2.templates.TemplateRuntime; + +/** + * Implements the configuration resource model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Resource { + + // rule paths + private List paths; + + // file types + private List filetypes; + + // the application language + private String language; + + // maximum number of loops + private long loops; + + // verbose flag + private boolean verbose; + + // logging flag + private boolean logging; + + // database name + private String dbname; + + // log name + private String logname; + + // header flag + private boolean header; + + // map of preambles + private Map preambles; + + // look and feel + private String laf; + + /** + * Gets the rule paths. + * @return The rule paths. + */ + public List getPaths() { + if (paths != null) { + + final Map map = new HashMap(); + Map user = new HashMap(); + user.put("home", SystemUtils.USER_HOME); + user.put("dir", SystemUtils.USER_DIR); + user.put("name", SystemUtils.USER_NAME); + map.put("user", user); + + Collection result = CollectionUtils.collect( + paths, new Transformer() { + public String transform(String input) { + String path = CommonUtils.removeKeyword(input); + try { + path = (String) TemplateRuntime.eval(path, map); + } + catch (RuntimeException nothandled) { + // do nothing, gracefully fallback to + // the default, unparsed path + } + return path; + } + }); + paths = new ArrayList(result); + } + return paths; + } + + /** + * Sets the rule paths. + * @param paths The rule paths. + */ + public void setPaths(List paths) { + this.paths = paths; + } + + /** + * Gets the list of file types. + * @return The list of file types. + */ + public List getFiletypes() { + return filetypes; + } + + /** + * Sets the list of file types. + * @param filetypes The list of file types. + */ + public void setFiletypes(List filetypes) { + this.filetypes = filetypes; + } + + /** + * Gets the language. + * @return The language. + */ + public String getLanguage() { + return CommonUtils.removeKeyword(language); + } + + /** + * Sets the language. + * @param language The language. + */ + public void setLanguage(String language) { + this.language = language; + } + + /** + * Get the maximum number of loops. + * @return The maximum number of loops. + */ + public long getLoops() { + return loops; + } + + /** + * Sets the maximum number of loops. + * @param loops The maximum number of loops. + */ + public void setLoops(long loops) { + this.loops = loops; + } + + /** + * Checks if verbose mode is active. + * @return A boolean value. + */ + public boolean isVerbose() { + return verbose; + } + + /** + * Sets the verbose mode. + * @param verbose A boolean value. + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + /** + * Checks if logging mode is active. + * @return A boolean value. + */ + public boolean isLogging() { + return logging; + } + + /** + * Sets the logging mode. + * @param logging A boolean value. + */ + public void setLogging(boolean logging) { + this.logging = logging; + } + + /** + * Gets the database name. + * @return The database name. + */ + public String getDbname() { + return CommonUtils.removeKeyword(dbname); + } + + /** + * Sets the database name. + * @param dbname The database name. + */ + public void setDbname(String dbname) { + this.dbname = dbname; + } + + /** + * Gets the log name. + * @return The log name. + */ + public String getLogname() { + return CommonUtils.removeKeyword(logname); + } + + /** + * Sets the log name. + * @param logname The log name. + */ + public void setLogname(String logname) { + this.logname = logname; + } + + /** + * Gets the map of preambles. + * @return Map of preambles. + */ + public Map getPreambles() { + return preambles; + } + + /** + * Sets the map of preambles. + * @param preambles Map of preambles. + */ + public void setPreambles(Map preambles) { + this.preambles = preambles; + } + + /** + * Gets the logical value of the header flag. + * @return Logical value of the header flag. + */ + public boolean isHeader() { + return header; + } + + /** + * Sets the logical value of the header flag. + * @param header The header flag. + */ + public void setHeader(boolean header) { + this.header = header; + } + + /** + * Gets the look and feel reference. + * @return The look and feel reference. + */ + public String getLaf() { + return CommonUtils.removeKeyword(laf); + } + + /** + * Sets the look and feel reference. + * @param laf The look and feel reference. + */ + public void setLaf(String laf) { + this.laf = laf; + } + + + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Rule.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Rule.java new file mode 100644 index 0000000000..af3ad19950 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Rule.java @@ -0,0 +1,155 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.utils.CommonUtils; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; + +/** + * Implements the rule model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Rule { + + // the rule identifier + private String identifier; + + // the rule name + private String name; + + // the list of authors + private List authors; + + // the list of commands + private List commands; + + // the list of arguments + private List arguments; + + /** + * Gets the rule identifier. + * @return The rule identifier. + */ + public String getIdentifier() { + return CommonUtils.removeKeyword(identifier); + } + + /** + * Sets the rule identifier. + * @param identifier The rule identifier. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Gets the rule identifier. + * @return The rule identifier. + */ + public String getName() { + return CommonUtils.removeKeyword(name); + } + + /** + * Sets the rule name. + * @param name The rule name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the list of authors. + * @return A list of authors. + */ + public List getAuthors() { + if (authors != null) { + Collection result = CollectionUtils.collect( + authors, new Transformer() { + public String transform(String input) { + return CommonUtils.removeKeyword(input); + } + }); + authors = new ArrayList(result); + } + return authors; + } + + /** + * Sets the list of authors. + * @param authors The list of authors. + */ + public void setAuthors(List authors) { + this.authors = authors; + } + + /** + * Gets the list of commands. + * @return The list of commands. + */ + public List getCommands() { + return commands; + } + + /** + * Sets the list of commands. + * @param commands The list of commands. + */ + public void setCommands(List commands) { + this.commands = commands; + } + + /** + * Gets the list of arguments. + * @return The list of arguments. + */ + public List getArguments() { + return arguments; + } + + /** + * Sets the list of arguments. + * @param arguments The list of arguments. + */ + public void setArguments(List arguments) { + this.arguments = arguments; + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/RuleCommand.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/RuleCommand.java new file mode 100644 index 0000000000..b209d19bcb --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/RuleCommand.java @@ -0,0 +1,103 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.utils.CommonUtils; + +/** + * Implements the rule command model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class RuleCommand { + + // the command name + private String name; + + // the command instruction + private String command; + + // the exit status expression + private String exit; + + /** + * Gets the command name. + * @return The command name. + */ + public String getName() { + return CommonUtils.removeKeyword(name); + } + + /** + * Sets the command name. + * @param name The command name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the command instruction. + * @return The command instruction. + */ + public String getCommand() { + return CommonUtils.removeKeyword(command); + } + + /** + * Sets the command instruction. + * @param command The command instruction. + */ + public void setCommand(String command) { + this.command = command; + } + + /** + * Gets the exit status expression. + * @return The exit status expression. + */ + public String getExit() { + return CommonUtils.removeKeyword(exit); + } + + /** + * Sets the exit status expression. + * @param exit The exit status expression. + */ + public void setExit(String exit) { + this.exit = exit; + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Session.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Session.java new file mode 100644 index 0000000000..c756ae4550 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Session.java @@ -0,0 +1,122 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.controller.SessionController; + +/** + * Implements the session model. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Session { + + // get the current instance from the + // session controller + private static final SessionController session = + SessionController.getInstance(); + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Inserts the object into the session, indexed by the provided key. + * @param key The provided key. + * @param value The value to be inserted. + */ + public void insert(String key, Object value) { + session.put(key, value); + } + + /** + * Removes the entry indexed by the provided key from the session. + * @param key The provided key. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public void remove(String key) throws AraraException { + if (session.contains(key)) { + session.remove(key); + } else { + throw new 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. + */ + public boolean exists(String key) { + return session.contains(key); + } + + /** + * Clears the session. + */ + public void forget() { + session.clear(); + } + + /** + * Gets the object indexed by the provided key from the session. + * @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. + */ + public Object obtain(String key) throws AraraException { + if (session.contains(key)) { + return session.get(key); + } else { + throw new AraraException( + messages.getMessage( + Messages.ERROR_SESSION_OBTAIN_UNKNOWN_KEY, + key + ) + ); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/StopWatch.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/StopWatch.java new file mode 100644 index 0000000000..45e0969d0b --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/StopWatch.java @@ -0,0 +1,84 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.ConfigurationController; + +/** + * Implements a stopwatch. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class StopWatch { + + // two variables indicating the + // times the stopwatch started + // and stopped + private static long beginning = 0; + private static long end = 0; + + // a variable to indicate the + // stopwatch is enabled; so far, + // it hasn't started, then it is + // not enabled + private static boolean enabled = false; + + /** + * Starts the stopwatch. + */ + public static void start() { + beginning = System.nanoTime(); + enabled = true; + } + + /** + * Stops the stopwatch. + */ + public static void stop() { + end = System.nanoTime(); + } + + /** + * Gets the string representation of the elapsed time. + * @return A string representation of the elapsed time. + */ + public static String getTime() { + Language language = (Language) ConfigurationController. + getInstance().get("execution.language"); + double result = enabled ? (double) (end - beginning) / 1000000000 : 0.0; + return String.format(language.getLocale(), "%1.2f", result); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/model/Trigger.java b/support/arara/source/src/main/java/com/github/cereda/arara/model/Trigger.java new file mode 100644 index 0000000000..11da04bfcb --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/model/Trigger.java @@ -0,0 +1,135 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.model; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +/** + * Implements the trigger model. The tool provides triggers, which are a way + * to alter its internal behaviour according to a list of parameters. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Trigger { + + // the action name and its + // list of parameters + private final String action; + private final List parameters; + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Constructor. + * @param action The action name. + * @param parameters The list of parameters. + */ + public Trigger(String action, List parameters) { + this.action = action; + this.parameters = parameters; + } + + /** + * Gets the action name. + * @return The action name. + */ + public String getAction() { + return action; + } + + /** + * Gets the list of parameters. + * @return The list of parameters. + */ + public List getParameters() { + return parameters; + } + + /** + * Returns a textual representation of the current trigger. + * @return A string containing a textual representation of the current + * trigger. + */ + @Override + public String toString() { + return "trigger"; + } + + /** + * Processes the current trigger. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public void process() throws AraraException { + + Map> mapping = + new HashMap>(); + mapping.put("halt", new Callable() { + public Object call() { + ConfigurationController.getInstance().put("trigger.halt", true); + return null; + } + }); + if (mapping.containsKey(action)) { + try { + mapping.get(action).call(); + } catch (Exception exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_TRIGGER_CALL_EXCEPTION, + action + ), + exception + ); + } + } else { + throw new AraraException( + messages.getMessage( + Messages.ERROR_TRIGGER_ACTION_NOT_FOUND, + action + ) + ); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/ClassLoadingUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/ClassLoadingUtils.java new file mode 100644 index 0000000000..c40536ce08 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/ClassLoadingUtils.java @@ -0,0 +1,166 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.model.Pair; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Implements utilitary methods for classloading and object instantiation. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class ClassLoadingUtils { + + /** + * 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. + */ + public static Pair loadClass(File file, String name) { + + // status and class to be returned, + // it defaults to an object class + int status = 0; + Class value = Object.class; + + // if file does not exist, nothing + // can be done, status is changed + if (!file.exists()) { + status = 1; + } 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) + URLClassLoader classloader = new URLClassLoader( + new URL[]{ + file.toURI().toURL() + }, + ClassLoadingUtils.class.getClassLoader() + ); + + // fetches the class from the + // instantiated classloader + value = Class.forName(name, true, classloader); + + } catch (MalformedURLException nothandled1) { + + // the file URL is incorrect, + // update status accordingly + status = 2; + + } catch (ClassNotFoundException nothandled2) { + + // the class was not found, + // update status accordingly + status = 3; + + } + } + + // return a new pair based on the + // current status and class holder + return new Pair(status, 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. + */ + public static Pair loadObject(File file, String name) { + + // load the corresponding class + // based on the qualified name + Pair pair = loadClass(file, name); + + // status and object to be returned, + // it defaults to an object + int status = pair.getFirstElement(); + Object value = new Object(); + + // checks if the class actually + // exists, otherwise simply + // ignore instantiation + if (status == 0) { + + // 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.getSecondElement().newInstance(); + + } catch (IllegalAccessException nothandled1) { + + // the object instantiation violated + // an access policy, status is updated + status = 4; + + } catch (InstantiationException nothandled2) { + + // an instantiation exception has + // occurred, status is updated + status = 5; + + } + } + + // return a new pair based on the + // current status and object holder + return new Pair(status, value); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/CommonUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/CommonUtils.java new file mode 100644 index 0000000000..621634749e --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/CommonUtils.java @@ -0,0 +1,929 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.controller.SystemCallController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Argument; +import com.github.cereda.arara.model.Database; +import com.github.cereda.arara.model.FileType; +import com.github.cereda.arara.model.Messages; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.MissingFormatArgumentException; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.NameFileFilter; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.SystemUtils; + +/** + * Implements common utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class CommonUtils { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * 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. + */ + public static boolean checkBoolean(String value) throws AraraException { + List yes = Arrays.asList( + new String[]{"yes", "true", "1", "on"} + ); + List no = Arrays.asList( + new String[]{"no", "false", "0", "off"} + ); + if (!union(yes, no).contains(value.toLowerCase())) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CHECKBOOLEAN_NOT_VALID_BOOLEAN, + value + ) + ); + } else { + return yes.contains(value.toLowerCase()); + } + } + + /** + * Provides a union set operation between two lists. + * @param The list type. + * @param list1 The first list. + * @param list2 The second list. + * @return The union of those two lists. + */ + private static List union(List list1, List list2) { + Set elements = new HashSet(); + elements.addAll(list1); + elements.addAll(list2); + return new ArrayList(elements); + } + + /** + * Build a system-dependant path based on the path and the file. + * @param path A string representing the path to be prepended. + * @param file A string representing the file to be appended. + * @return The full path as a string. + */ + public static String buildPath(String path, String file) { + return path.endsWith(File.separator) ? + path.concat(file) : path.concat(File.separator).concat(file); + } + + /** + * Checks if the provided string is empty. It does not handle a null value. + * @param string A string. + * @return A boolean value indicating if the string is empty. + */ + public static boolean checkEmptyString(String string) { + return "".equals(string); + } + + /** + * Removes the keyword from the beginning of the provided string. + * @param line A string to be analyzed. + * @return The provided string without the keyword. + */ + public static String removeKeyword(String line) { + if (line != null) { + Pattern pattern = Pattern.compile("^(\\s)*\\s"); + Matcher matcher = pattern.matcher(line); + if (matcher.find()) { + line = (line.substring(matcher.end(), line.length())); + } + line = line.trim(); + } + return line; + } + + /** + * 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. + */ + public static void discoverFile(String reference) throws AraraException { + File file = lookupFile(reference); + if (file == null) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_DISCOVERFILE_FILE_NOT_FOUND, + reference, + getFileTypesList() + ) + ); + } + } + + /** + * 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. + */ + private static File lookupFile(String reference) throws AraraException { + @SuppressWarnings("unchecked") + List types = (List) ConfigurationController. + getInstance().get("execution.filetypes"); + File file = new File(reference); + String name = file.getName(); + String parent = getParentCanonicalPath(file); + String path = buildPath(parent, name); + + // direct search, so we are considering + // the reference as a complete name + for (FileType type : types) { + if (path.endsWith(".".concat(type.getExtension()))) { + file = new File(path); + if (file.exists()) { + if (file.isFile()) { + ConfigurationController. + getInstance(). + put("execution.file.pattern", + type.getPattern()); + ConfigurationController. + getInstance(). + put("execution.reference", file); + return file; + } + } + } + } + + // indirect search; in this case, we are considering + // that the file reference has an implict extension, + // so we need to add it and look again + for (FileType type : types) { + path = buildPath(parent, name.concat("."). + concat(type.getExtension())); + file = new File(path); + if (file.exists()) { + if (file.isFile()) { + ConfigurationController.getInstance(). + put("execution.file.pattern", type.getPattern()); + ConfigurationController.getInstance(). + put("execution.reference", file); + return file; + } + } + } + return null; + } + + /** + * 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. + */ + public static String getParentCanonicalPath(File file) + throws AraraException { + try { + String path = file.getCanonicalFile().getParent(); + return path; + } catch (IOException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_GETPARENTCANONICALPATH_IO_EXCEPTION + ), + exception + ); + } + } + + /** + * Gets the canonical file from a file. + * @param file The file. + * @return The canonical file. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static File getCanonicalFile(String file) throws AraraException { + try { + return (new File(file)).getCanonicalFile(); + } catch (IOException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_GETCANONICALFILE_IO_EXCEPTION + ), + exception + ); + } + } + + /** + * Checks if the provided object is from a certain class. + * @param clazz The class. + * @param object The object. + * @return A boolean value indicating if the provided object is from a + * certain class. + */ + public static boolean checkClass(Class clazz, Object object) { + return clazz.isInstance(object); + } + + /** + * Helper method to flatten a potential list of lists into a list of + * objects. + * @param list First list. + * @param flat Second list. + */ + private static void flatten(List list, List flat) { + for (Object item : list) { + if (item instanceof List) { + flatten((List) item, flat); + } else { + flat.add(item); + } + } + } + + /** + * Flattens a potential list of lists into a list of objects. + * @param list The list to be flattened. + * @return The flattened list. + */ + public static List flatten(List list) { + List result = new ArrayList(); + flatten(list, result); + return result; + } + + /** + * Gets the list of file types, in order. + * @return A string representation of the list of file types, in order. + */ + public static String getFileTypesList() { + @SuppressWarnings("unchecked") + List types = (List) ConfigurationController. + getInstance().get("execution.filetypes"); + return getCollectionElements(types, "[ ", " ]", " | "); + } + + /** + * Gets a string representation of a collection. + * @param collection The collection. + * @param open The opening string. + * @param close The closing string. + * @param separator The element separator. + * @return A string representation of the provided collection. + */ + public static String getCollectionElements(Collection collection, + String open, String close, String separator) { + StringBuilder builder = new StringBuilder(); + builder.append(open); + builder.append(StringUtils.join(collection, separator)); + builder.append(close); + return builder.toString(); + } + + /** + * 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. + */ + public static Set getUnknownKeys(Map parameters, + List arguments) { + Collection found = parameters.keySet(); + Collection expected = CollectionUtils.collect( + arguments, new Transformer() { + public String transform(Argument argument) { + return argument.getIdentifier(); + } + }); + Collection difference = CollectionUtils. + subtract(found, expected); + return new HashSet(difference); + } + + /** + * 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. + */ + public static String getRuleErrorHeader() { + if ((ConfigurationController.getInstance(). + contains("execution.info.rule.id")) && + (ConfigurationController.getInstance(). + contains("execution.info.rule.path"))) { + String id = (String) ConfigurationController.getInstance(). + get("execution.info.rule.id"); + String path = (String) ConfigurationController.getInstance(). + get("execution.info.rule.path"); + return messages.getMessage( + Messages.ERROR_RULE_IDENTIFIER_AND_PATH, + id, + path + ).concat(" "); + } else { + return ""; + } + } + + /** + * Trims spaces from every string of a list of strings. + * @param input The list of strings. + * @return A new list of strings, with each element trimmed. + */ + public static List trimSpaces(List input) { + Collection result = CollectionUtils.collect( + input, new Transformer() { + public String transform(String input) { + return input.trim(); + } + }); + return new ArrayList(result); + } + + /** + * Gets a human readable representation of a file size. + * @param file The file. + * @return A string representation of the file size. + */ + public static String calculateFileSize(File file) { + return FileUtils.byteCountToDisplaySize(file.length()); + } + + /** + * 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. + */ + public static String getLastModifiedInformation(File file) { + SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + return format.format(file.lastModified()); + } + + /** + * 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. + */ + public static List getAllRulePaths() throws AraraException { + @SuppressWarnings("unchecked") + List paths = (List) ConfigurationController. + getInstance().get("execution.rule.paths"); + List result = new ArrayList(); + for (String path : paths) { + File location = new File(InterpreterUtils.construct(path, "quack")); + result.add(getParentCanonicalPath(location)); + } + return result; + } + + /** + * 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 static File getCurrentReference() { + return (File) ConfigurationController.getInstance(). + get("execution.file"); + } + + /** + * 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. + */ + public static String calculateHash(File file) throws AraraException { + try { + long result = FileUtils.checksumCRC32(file); + return String.format("%08x", result); + } catch (IOException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CALCULATEHASH_IO_EXCEPTION + ), + exception + ); + } + } + + /** + * Gets the file type of a file. + * @param file The file. + * @return The corresponding file type. + */ + public static String getFiletype(File file) { + return getFiletype(file.getName()); + } + + /** + * Gets the file type of a string representing the file. + * @param name A string representing the file. + * @return The corresponding file type. + */ + public static String getFiletype(String name) { + name = name.lastIndexOf(".") != -1 ? + name.substring(name.lastIndexOf(".") + 1, name.length()) : ""; + return name; + } + + /** + * Gets the base name of a file. + * @param file The file. + * @return The corresponding base name. + */ + public static String getBasename(File file) { + return getBasename(file.getName()); + } + + /** + * Gets the base name of a string representing the file. + * @param name A string representing the file. + * @return The corresponding base name. + */ + public static String getBasename(String name) { + int index = name.lastIndexOf(".") != -1 ? + name.lastIndexOf(".") : name.length(); + return name.substring(0, index); + } + + /** + * Encloses the provided object in double quotes. + * @param object The object. + * @return A string representation of the provided object enclosed in double + * quotes. + */ + public static String addQuotes(Object object) { + return "\"".concat(String.valueOf(object)).concat("\""); + } + + /** + * 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. + */ + public static String generateString(Object... objects) { + List values = new ArrayList(); + for (Object object : objects) { + if (!CommonUtils.checkEmptyString(String.valueOf(object))) { + values.add(String.valueOf(object)); + } + } + return StringUtils.join(values, " "); + } + + /** + * Checks if a file exists. + * @param file The file. + * @return A boolean value indicating if the file exists. + */ + public static boolean exists(File file) { + return file.exists(); + } + + /** + * 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. + */ + public static boolean exists(String extension) throws AraraException { + File file = new File(getPath(extension)); + return file.exists(); + } + + /** + * 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. + */ + public static boolean hasChanged(File file) throws AraraException { + Database database = DatabaseUtils.load(); + HashMap map = database.getMap(); + String path = getCanonicalPath(file); + if (!file.exists()) { + if (map.containsKey(path)) { + map.remove(path); + database.setMap(map); + DatabaseUtils.save(database); + return true; + } else { + return false; + } + } else { + String hash = calculateHash(file); + if (map.containsKey(path)) { + String value = map.get(path); + if (hash.equals(value)) { + return false; + } else { + map.put(path, hash); + database.setMap(map); + DatabaseUtils.save(database); + return true; + } + } else { + map.put(path, hash); + database.setMap(map); + DatabaseUtils.save(database); + return 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. + */ + public static boolean hasChanged(String extension) throws AraraException { + File file = new File(getPath(extension)); + return hasChanged(file); + } + + /** + * 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. + */ + private static String getPath(String extension) throws AraraException { + String name = getBasename(getCurrentReference()); + String path = getParentCanonicalPath(getCurrentReference()); + name = name.concat(".").concat(extension); + return buildPath(path, name); + } + + /** + * 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. + */ + public static String getCanonicalPath(File file) throws AraraException { + try { + return file.getCanonicalPath(); + } catch (IOException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_GETCANONICALPATH_IO_EXCEPTION + ), + exception + ); + } + } + + /** + * 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. + */ + public static boolean checkRegex(String extension, String regex) + throws AraraException { + File file = new File(getPath(extension)); + return checkRegex(file, regex); + } + + /** + * Checks if the file contains the provided regex. + * @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. + */ + public static boolean checkRegex(File file, String regex) + throws AraraException { + Charset charset = (Charset) ConfigurationController. + getInstance().get("directives.charset"); + try { + String text = FileUtils.readFileToString(file, charset.name()); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(text); + return matcher.find(); + } catch (IOException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CHECKREGEX_IO_EXCEPTION, + file.getName() + ), + 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. + */ + public static List replicateList(String pattern, + List values) throws AraraException { + List result = new ArrayList(); + for (Object value : values) { + try { + result.add(String.format(pattern, value)); + } catch (MissingFormatArgumentException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_REPLICATELIST_MISSING_FORMAT_ARGUMENTS_EXCEPTION + ), + exception + ); + } + } + return result; + } + + /** + * Checks if the provided operating system string holds according to the + * underlying operating system. + * @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. + */ + public static boolean checkOS(String value) throws AraraException { + Map values = new HashMap(); + values.put("windows", SystemUtils.IS_OS_WINDOWS); + values.put("linux", SystemUtils.IS_OS_LINUX); + values.put("mac", SystemUtils.IS_OS_MAC_OSX); + values.put("unix", SystemUtils.IS_OS_UNIX); + values.put("aix", SystemUtils.IS_OS_AIX); + values.put("irix", SystemUtils.IS_OS_IRIX); + values.put("os2", SystemUtils.IS_OS_OS2); + values.put("solaris", SystemUtils.IS_OS_SOLARIS); + values.put("cygwin", (Boolean) SystemCallController. + getInstance().get("cygwin")); + if (!values.containsKey(value.toLowerCase())) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CHECKOS_INVALID_OPERATING_SYSTEM, + value + ) + ); + } + return values.get(value.toLowerCase()); + } + + /** + * Returns the exit status of the application. + * @return An integer representing the exit status of the application. + */ + public static int getExitStatus() { + return (Integer) ConfigurationController. + getInstance().get("execution.status"); + } + + /** + * 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. + */ + public static String getSystemProperty(String key, String fallback) { + try { + String result = System.getProperty(key, fallback); + return result.equals("") ? fallback : result; + } catch (Exception exception) { + return fallback; + } + } + + /** + * 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. + */ + public static List getPreambleContent() { + if (((Boolean) ConfigurationController. + getInstance().get("execution.preamble.active")) == true) { + return new ArrayList( + Arrays.asList( + ((String) ConfigurationController.getInstance(). + get("execution.preamble.content") + ).split("\n")) + ); + } + else { + return new ArrayList(); + } + } + + /** + * 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 static List appendExtensions(String command) { + + // the resulting list, to hold the + // filenames generated from the + // provided command + List result = new ArrayList(); + + // list of extensions, specific for + // each operating system (in fact, it + // is more Windows specific) + List extensions; + + // the application is running on + // Windows, so let's look for the + // following extensions in order + if (SystemUtils.IS_OS_WINDOWS) { + + // this list is actually a sublist from + // the original Windows PATHEXT environment + // variable which holds the list of executable + // extensions that Windows supports + extensions = Arrays.asList(".com", ".exe", ".bat", ".cmd"); + } + else { + + // no Windows, so the default + // extension will be just an + // empty string + extensions = Arrays.asList(""); + } + + // for each and every extension in the + // list, let's build the corresponding + // filename and add to the result + for (String extension : extensions) { + result.add(command.concat(extension)); + } + + // return the resulting list + // holding the filenames + return result; + } + + /** + * Checks if the provided command name is reachable from the system path. + * @param command A string representing the command. + * @return A logic value. + */ + public static boolean isOnPath(String command) { + try { + + // first and foremost, let's build the list + // of filenames based on the underlying + // operating system + List filenames = appendExtensions(command); + + // break the path into several parts + // based on the path separator symbol + StringTokenizer tokenizer = new StringTokenizer( + System.getenv("PATH"), + File.pathSeparator + ); + + // iterate through every part of the + // path looking for each filename + while (tokenizer.hasMoreTokens()) { + + // 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 + if ( + !FileUtils.listFiles( + new File(tokenizer.nextToken()), + new NameFileFilter(filenames), + null + ).isEmpty()) { + + // command is found somewhere, + // so it is on path + return true; + } + } + + // nothing was found, + // command is not on path + return false; + } + catch (Exception exception) { + + // an exception was raised, simply + // return and forget about it + return false; + } + } + + /** + * Gets the full base name of a file. + * @param file The file. + * @return The corresponding full base name. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static String getFullBasename(File file) throws AraraException { + + // if the provided file does not contain a + // file separator, fallback to the usual + // base name lookup + if (!file.toString().contains(File.separator)) { + return getBasename(file); + } + else { + + // we need to get the parent file, get the + // canonical path and build the corresponding + // full base name path + File parent = file.getParentFile(); + String path = getCanonicalPath(parent == null ? file : parent); + return buildPath(path, getBasename(file)); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/ConfigurationUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/ConfigurationUtils.java new file mode 100644 index 0000000000..0b7d3b8a5b --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/ConfigurationUtils.java @@ -0,0 +1,239 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.Arara; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.FileType; +import com.github.cereda.arara.model.FileTypeResource; +import com.github.cereda.arara.model.Messages; +import com.github.cereda.arara.model.Resource; +import java.io.File; +import java.io.FileReader; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Predicate; +import org.apache.commons.lang.SystemUtils; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.error.MarkedYAMLException; +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.representer.Representer; + +/** + * Implements configuration utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class ConfigurationUtils { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Gets the list of default file types provided by nightingale, in order. + * @return The list of default file types, in order. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static List getDefaultFileTypes() throws AraraException { + return Arrays.asList( + new FileType("tex"), + new FileType("dtx"), + new FileType("ltx"), + new FileType("drv"), + new FileType("ins") + ); + } + + /** + * Gets the configuration file located at the user home directory, if any. + * @return The file reference to the external configuration, if any. + */ + public static File getConfigFile() { + List names = Arrays.asList( + ".araraconfig.yaml", + "araraconfig.yaml", + ".arararc.yaml", + "arararc.yaml" + ); + + // look for configuration files in the user's working directory first + for (String name : names) { + String path = CommonUtils.buildPath(SystemUtils.USER_DIR, name); + File file = new File(path); + if (file.exists()) { + return file; + } + } + + // 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 + for (String name : names) { + String path = CommonUtils.buildPath(SystemUtils.USER_HOME, name); + File file = new File(path); + if (file.exists()) { + return file; + } + } + return null; + } + + /** + * 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. + */ + public static Resource validateConfiguration(File file) + throws AraraException { + + Representer representer = new Representer(); + representer.addClassTag(Resource.class, new Tag("!config")); + Yaml yaml = new Yaml(new Constructor(Resource.class), representer); + try { + Resource resource = yaml.loadAs(new FileReader(file), + Resource.class); + if (resource.getFiletypes() != null) { + List filetypes = resource.getFiletypes(); + if (CollectionUtils.exists(filetypes, + new Predicate() { + public boolean evaluate(FileTypeResource filetype) { + return (filetype.getExtension() == null); + } + })) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CONFIGURATION_FILETYPE_MISSING_EXTENSION + ) + ); + } + } + return resource; + } catch (MarkedYAMLException yamlException) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CONFIGURATION_INVALID_YAML + ), + yamlException + ); + } catch (Exception exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_CONFIGURATION_GENERIC_ERROR + ) + ); + } + } + + /** + * 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. + */ + public static List normalizePaths(List paths) + throws AraraException { + paths.add(CommonUtils.buildPath(getApplicationPath(), "rules")); + Set set = new LinkedHashSet(paths); + List result = new ArrayList(set); + return result; + } + + /** + * 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. + */ + public static List normalizeFileTypes(List types) + throws AraraException { + types.addAll(getDefaultFileTypes()); + Set set = new LinkedHashSet(types); + List result = new ArrayList(set); + return result; + } + + /** + * Gets the canonical absolute application path. + * @return The string representation of the canonical absolute application + * path. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static String getApplicationPath() throws AraraException { + try { + String path = Arara.class.getProtectionDomain(). + getCodeSource().getLocation().getPath(); + path = URLDecoder.decode(path, "UTF-8"); + path = new File(path).getParentFile().getPath(); + return path; + } catch (UnsupportedEncodingException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_GETAPPLICATIONPATH_ENCODING_EXCEPTION + ), + exception + ); + } + } + + /** + * Cleans the file name to avoid invalid entries. + * @param name The file name. + * @return A cleaned file name. + */ + public static String cleanFileName(String name) { + String result = (new File(name)).getName().trim(); + if (CommonUtils.checkEmptyString(result)) { + return "arara"; + } else { + return result.trim(); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/DatabaseUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DatabaseUtils.java new file mode 100644 index 0000000000..8861ff7e5e --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DatabaseUtils.java @@ -0,0 +1,140 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Database; +import com.github.cereda.arara.model.Messages; +import java.io.File; +import org.simpleframework.xml.Serializer; +import org.simpleframework.xml.core.Persister; + +/** + * Implements database utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class DatabaseUtils { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Loads the XML file representing the database. + * @return The database object. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Database load() throws AraraException { + if (!exists()) { + return new Database(); + } else { + File file = new File(getPath()); + try { + Serializer serializer = new Persister(); + Database database = serializer.read(Database.class, file); + return database; + } catch (Exception exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_LOAD_COULD_NOT_LOAD_XML, + file.getName() + ), + exception + ); + } + } + } + + /** + * Saves the database on a XML file. + * @param database The database object. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static void save(Database database) throws AraraException { + File file = new File(getPath()); + try { + Serializer serializer = new Persister(); + serializer.write(database, file); + } catch (Exception exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_SAVE_COULD_NOT_SAVE_XML, + file.getName() + ), + exception + ); + } + } + + /** + * Checks if the XML file representing the database exists. + * @return A boolean value indicating if the XML file exists. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + private static boolean exists() throws AraraException { + File file = new File(getPath()); + return file.exists(); + } + + /** + * Gets the path to the XML file representing the database. + * @return A string representing the path to the XML file. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + private static String getPath() throws AraraException { + String name = ((String) ConfigurationController. + getInstance().get("execution.database.name")).concat(".xml"); + String path = CommonUtils.getParentCanonicalPath(getReference()); + return CommonUtils.buildPath(path, name); + } + + /** + * Gets the main file reference. + * @return The main file reference. + */ + private static File getReference() { + return (File) ConfigurationController. + getInstance().get("execution.reference"); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveAssembler.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveAssembler.java new file mode 100644 index 0000000000..d2dd588996 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveAssembler.java @@ -0,0 +1,106 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implements a directive assembler in order to help build a directive from a + * list of strings. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class DirectiveAssembler { + + // this variable holds a list of + // line numbers indicating which + // lines composed the resulting + // potential directive + private final List lineNumbers; + + // this variable holds the textual + // representation of the directive + private String text; + + /** + * Constructor. + */ + public DirectiveAssembler() { + lineNumbers = new ArrayList(); + text = ""; + } + + /** + * Checks if an append operation is allowed. + * @return A boolean value indicating if an append operation is allowed. + */ + public boolean isAppendAllowed() { + return !lineNumbers.isEmpty(); + } + + /** + * Adds a line number to the assembler. + * @param line An integer representing the line number. + */ + public void addLineNumber(int line) { + lineNumbers.add(line); + } + + /** + * Appends the provided line to the assembler text. + * @param line The provided line. + */ + public void appendLine(String line) { + text = text.concat(" ").concat(line.trim()); + } + + /** + * Gets the list of line numbers. + * @return The list of line numbers. + */ + public List getLineNumbers() { + return lineNumbers; + } + + /** + * Gets the text. + * @return The assembler text, properly trimmed. + */ + public String getText() { + return text.trim(); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveResolver.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveResolver.java new file mode 100644 index 0000000000..6ea18e31ba --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveResolver.java @@ -0,0 +1,59 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.resolver.Resolver; + +/** + * This class implements a directive resolver. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class DirectiveResolver extends Resolver { + + /** + * Adds implicit resolvers to the YAML model. For arara, I disabled + * boolean and numeric values to be automatically parsed. They still can + * be used through an explicit conversion in the rule context. + */ + @Override + protected void addImplicitResolvers() { + addImplicitResolver(Tag.MERGE, MERGE, "<"); + addImplicitResolver(Tag.NULL, NULL, "~nN\0"); + addImplicitResolver(Tag.NULL, EMPTY, null); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveUtils.java new file mode 100644 index 0000000000..d4d2a88ac7 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DirectiveUtils.java @@ -0,0 +1,438 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Conditional; +import com.github.cereda.arara.model.Directive; +import com.github.cereda.arara.model.Messages; +import com.github.cereda.arara.model.Pair; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.error.MarkedYAMLException; +import org.yaml.snakeyaml.representer.Representer; + +/** + * Implements directive utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class DirectiveUtils { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + // get the logger context from a factory + private static final Logger logger = + LoggerFactory.getLogger(DirectiveUtils.class); + + /** + * 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. + */ + public static List extractDirectives(List lines) + throws AraraException { + + boolean header = (Boolean) ConfigurationController. + getInstance().get("execution.header"); + String regex = (String) ConfigurationController. + getInstance().get("execution.file.pattern"); + Pattern linecheck = Pattern.compile(regex); + regex = regex.concat((String) ConfigurationController. + getInstance().get("application.pattern")); + Pattern pattern = Pattern.compile(regex); + List> pairs = + new ArrayList>(); + Matcher matcher; + for (int i = 0; i < lines.size(); i++) { + matcher = pattern.matcher(lines.get(i)); + if (matcher.find()) { + String line = lines.get(i).substring( + matcher.end(), + lines.get(i).length() + ); + Pair pair = + new Pair(i + 1, line); + pairs.add(pair); + + logger.info( + messages.getMessage( + Messages.LOG_INFO_POTENTIAL_PATTERN_FOUND, + (i + 1), + line.trim() + ) + ); + } + else { + if (header) { + if (!checkLinePattern(linecheck, lines.get(i))) { + break; + } + } + } + } + + if (pairs.isEmpty()) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_NO_DIRECTIVES_FOUND + ) + ); + } + + List assemblers + = new ArrayList(); + DirectiveAssembler assembler = new DirectiveAssembler(); + regex = (String) ConfigurationController.getInstance(). + get("directives.linebreak.pattern"); + pattern = Pattern.compile(regex); + for (Pair pair : pairs) { + matcher = pattern.matcher(pair.getSecondElement()); + if (matcher.find()) { + if (!assembler.isAppendAllowed()) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_ORPHAN_LINEBREAK, + pair.getFirstElement() + ) + ); + } else { + assembler.addLineNumber(pair.getFirstElement()); + assembler.appendLine(matcher.group(1)); + } + } else { + if (assembler.isAppendAllowed()) { + assemblers.add(assembler); + } + assembler = new DirectiveAssembler(); + assembler.addLineNumber(pair.getFirstElement()); + assembler.appendLine(pair.getSecondElement()); + } + } + if (assembler.isAppendAllowed()) { + assemblers.add(assembler); + } + + List directives = new ArrayList(); + for (DirectiveAssembler current : assemblers) { + directives.add(generateDirective(current)); + } + return directives; + + } + + /** + * 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. + */ + public static Directive generateDirective(DirectiveAssembler assembler) + throws AraraException { + String regex = (String) ConfigurationController.getInstance(). + get("directives.pattern"); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(assembler.getText()); + if (matcher.find()) { + Directive directive = new Directive(); + directive.setIdentifier(matcher.group(1)); + directive.setParameters( + getParameters(matcher.group(3), assembler.getLineNumbers()) + ); + Conditional conditional = new Conditional(); + conditional.setType(getType(matcher.group(5))); + conditional.setCondition(getCondition(matcher.group(6))); + directive.setConditional(conditional); + directive.setLineNumbers(assembler.getLineNumbers()); + + logger.info( + messages.getMessage( + Messages.LOG_INFO_POTENTIAL_DIRECTIVE_FOUND, + directive + ) + ); + + return directive; + } else { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_INVALID_DIRECTIVE_FORMAT, + CommonUtils.getCollectionElements( + assembler.getLineNumbers(), + "(", + ")", + ", " + ) + ) + ); + } + } + + /** + * Gets the conditional type based on the input string. + * @param text The input string. + * @return The conditional type. + */ + private static Conditional.ConditionalType getType(String text) { + if (text == null) { + return Conditional.ConditionalType.NONE; + } else { + if (text.equals("if")) { + return Conditional.ConditionalType.IF; + } else { + if (text.equals("while")) { + return Conditional.ConditionalType.WHILE; + } else { + if (text.equals("until")) { + return Conditional.ConditionalType.UNTIL; + } else { + return Conditional.ConditionalType.UNLESS; + } + } + } + } + } + + /** + * Gets the condition from the input string. + * @param text The input string. + * @return A string representing the condition. + */ + private static String getCondition(String text) { + return text == null ? "" : text; + } + + /** + * 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. + */ + private static Map getParameters(String text, + List numbers) throws AraraException { + + if (text == null) { + return new HashMap(); + } + + Yaml yaml = new Yaml( + new Constructor(), + new Representer(), + new DumperOptions(), + new DirectiveResolver() + ); + try { + @SuppressWarnings("unchecked") + HashMap map = yaml.loadAs(text, HashMap.class); + return map; + } catch (MarkedYAMLException exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_YAML_EXCEPTION, + CommonUtils.getCollectionElements( + numbers, + "(", + ")", + ", " + ) + ), + exception + ); + } + } + + /** + * 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. + */ + public static List validate(List directives) + throws AraraException { + + ArrayList result = new ArrayList(); + for (Directive directive : directives) { + Map parameters = directive.getParameters(); + + if (parameters.containsKey("file")) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_FILE_IS_RESERVED, + CommonUtils.getCollectionElements( + directive.getLineNumbers(), + "(", + ")", + ", " + ) + ) + ); + } + + if (parameters.containsKey("reference")) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_REFERENCE_IS_RESERVED, + CommonUtils.getCollectionElements( + directive.getLineNumbers(), + "(", + ")", + ", " + ) + ) + ); + } + + if (parameters.containsKey("files")) { + + Object holder = parameters.get("files"); + if (holder instanceof List) { + @SuppressWarnings("unchecked") + List files = (List) holder; + parameters.remove("files"); + if (files.isEmpty()) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_EMPTY_FILES_LIST, + CommonUtils.getCollectionElements( + directive.getLineNumbers(), + "(", + ")", + ", " + ) + ) + ); + } + for (Object file : files) { + Map map = new HashMap(); + for (String key : parameters.keySet()) { + map.put(key, parameters.get(key)); + } + File representation = CommonUtils. + getCanonicalFile(String.valueOf(file)); + + map.put("reference", representation); + map.put("file", representation.getName()); + + Directive addition = new Directive(); + Conditional conditional = new Conditional(); + conditional.setCondition(directive.getConditional(). + getCondition() + ); + conditional.setType(directive.getConditional(). + getType() + ); + addition.setIdentifier(directive.getIdentifier()); + addition.setConditional(conditional); + addition.setParameters(map); + addition.setLineNumbers(directive.getLineNumbers()); + result.add(addition); + } + } else { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VALIDATE_FILES_IS_NOT_A_LIST, + CommonUtils.getCollectionElements( + directive.getLineNumbers(), + "(", + ")", + ", " + ) + ) + ); + } + } else { + File representation = (File) ConfigurationController. + getInstance().get("execution.reference"); + parameters.put("file", representation.getName()); + parameters.put("reference", representation); + directive.setParameters(parameters); + result.add(directive); + } + } + + logger.info( + messages.getMessage( + Messages.LOG_INFO_VALIDATED_DIRECTIVES + ) + ); + logger.info(DisplayUtils.displayOutputSeparator( + messages.getMessage( + Messages.LOG_INFO_DIRECTIVES_BLOCK + ) + )); + for (Directive directive : result) { + logger.info(directive.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 static boolean checkLinePattern(Pattern pattern, String line) { + return CommonUtils.checkEmptyString(line.trim()) || + pattern.matcher(line).find(); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/DisplayUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DisplayUtils.java new file mode 100644 index 0000000000..879119e60e --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/DisplayUtils.java @@ -0,0 +1,502 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Conditional; +import com.github.cereda.arara.model.Messages; +import com.github.cereda.arara.model.StopWatch; +import java.io.File; +import java.util.List; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implements display utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class DisplayUtils { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + // get the logger context from a factory + private static final Logger logger = + LoggerFactory.getLogger(DisplayUtils.class); + + /** + * Displays the short version of the current entry in the terminal. + * @param name Rule name. + * @param task Task name. + */ + private static void buildShortEntry(String name, String task) { + int width = getWidth(); + int result = getLongestMatch(); + if (result >= width) { + result = 10; + } + int space = width - result - 1; + StringBuilder entry = new StringBuilder(); + entry.append("(").append(name).append(") "); + entry.append(task).append(" "); + String line = StringUtils.abbreviate(entry.toString(), space - 4); + entry = new StringBuilder(); + entry.append(StringUtils.rightPad(line, space, ".")).append(" "); + System.out.print(entry); + } + + /** + * Displays the short version of the current entry result in the terminal. + * @param value The boolean value to be displayed. + */ + private static void buildShortResult(boolean value) { + int result = getLongestMatch(); + System.out.println(StringUtils.leftPad(getResult(value), result)); + } + + /** + * Displays the current entry result in the terminal. + * @param value The boolean value to be displayed. + */ + public static void printEntryResult(boolean value) { + ConfigurationController.getInstance().put("display.line", false); + ConfigurationController.getInstance().put("display.result", true); + ConfigurationController.getInstance() + .put("execution.status", value ? 0 : 1); + logger.info( + messages.getMessage( + Messages.LOG_INFO_TASK_RESULT + ).concat(" ").concat(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 static void buildLongResult(boolean value) { + int width = getWidth(); + System.out.println("\n".concat(StringUtils.leftPad( + " ".concat(getResult(value)), width, "-" + ))); + } + + /** + * Displays the current entry in the terminal. + * @param name The rule name. + * @param task The task name. + */ + public static void printEntry(String name, String task) { + logger.info( + messages.getMessage( + Messages.LOG_INFO_INTERPRET_TASK, + task, + name + ) + ); + ConfigurationController.getInstance().put("display.line", true); + ConfigurationController.getInstance().put("display.result", false); + if (!isDryRunMode()) { + if (!isVerboseMode()) { + buildShortEntry(name, task); + } else { + buildLongEntry(name, task); + } + } else { + buildDryRunEntry(name, task); + } + } + + /** + * Gets the length of the longest result match. + * @return An integer value representing the longest result match. + */ + private static int getLongestMatch() { + String[] values = new String[]{ + messages.getMessage(Messages.INFO_LABEL_ON_SUCCESS), + messages.getMessage(Messages.INFO_LABEL_ON_FAILURE), + messages.getMessage(Messages.INFO_LABEL_ON_ERROR) + }; + int max = values[0].length(); + for (String value : values) { + if (max < value.length()) { + max = value.length(); + } + } + return max; + } + + /** + * Displays a long version of the current entry in the terminal. + * @param name Rule name. + * @param task Task name. + */ + private static void buildLongEntry(String name, String task) { + if (ConfigurationController.getInstance().contains("display.rolling")) { + addNewLine(); + } else { + ConfigurationController.getInstance().put("display.rolling", true); + } + StringBuilder line = new StringBuilder(); + line.append("(").append(name).append(") "); + line.append(task); + System.out.println(displaySeparator()); + System.out.println(StringUtils.abbreviate(line.toString(), getWidth())); + System.out.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 static void buildDryRunEntry(String name, String task) { + if (ConfigurationController.getInstance().contains("display.rolling")) { + addNewLine(); + } else { + ConfigurationController.getInstance().put("display.rolling", true); + } + StringBuilder line = new StringBuilder(); + line.append("[DR] (").append(name).append(") "); + line.append(task); + System.out.println(StringUtils.abbreviate(line.toString(), getWidth())); + System.out.println(displaySeparator()); + } + + /** + * Displays the exception in the terminal. + * @param exception The exception object. + */ + public static void printException(AraraException exception) { + ConfigurationController.getInstance().put("display.exception", true); + ConfigurationController.getInstance().put("execution.status", 2); + boolean display = false; + if (ConfigurationController.getInstance().contains("display.line")) { + display = (Boolean) ConfigurationController. + getInstance().get("display.line"); + } + if (ConfigurationController.getInstance().contains("display.result")) { + if (((Boolean) ConfigurationController. + getInstance().get("display.result")) == true) { + addNewLine(); + } + } + if (display) { + if (!isDryRunMode()) { + if (!isVerboseMode()) { + buildShortError(); + } else { + buildLongError(); + } + addNewLine(); + } + } + String text = exception.hasException() ? + exception.getMessage().concat(" ").concat( + messages.getMessage( + Messages.INFO_DISPLAY_EXCEPTION_MORE_DETAILS + ) + ) + : exception.getMessage(); + logger.error(text); + wrapText(text); + if (exception.hasException()) { + addNewLine(); + displayDetailsLine(); + String details = exception.getException().getMessage(); + logger.error(details); + wrapText(details); + } + } + + /** + * Gets the string representation of the provided boolean value. + * @param value The boolean value. + * @return The string representation. + */ + private static String getResult(boolean value) { + return (value == true ? + messages.getMessage( + Messages.INFO_LABEL_ON_SUCCESS + ) + : messages.getMessage(Messages.INFO_LABEL_ON_FAILURE)); + } + + /** + * Displays the short version of an error in the terminal. + */ + private static void buildShortError() { + int result = getLongestMatch(); + System.out.println(StringUtils.leftPad( + messages.getMessage( + Messages.INFO_LABEL_ON_ERROR + ), + result + )); + } + + /** + * Displays the long version of an error in the terminal. + */ + private static void buildLongError() { + String line = StringUtils.leftPad( + " ".concat(messages.getMessage(Messages.INFO_LABEL_ON_ERROR)), + getWidth(), "-"); + System.out.println(line); + } + + /** + * Gets the default terminal width defined in the settings. + * @return An integer representing the terminal width. + */ + private static int getWidth() { + return (Integer) ConfigurationController. + getInstance().get("application.width"); + } + + /** + * Displays the provided text wrapped nicely according to the default + * terminal width. + * @param text The text to be displayed. + */ + public static void wrapText(String text) { + System.out.println(WordUtils.wrap(text, getWidth())); + } + + /** + * Checks if the execution is in dry-run mode. + * @return A boolean value indicating if the execution is in dry-run mode. + */ + private static boolean isDryRunMode() { + return (Boolean) ConfigurationController. + getInstance().get("execution.dryrun"); + } + + /** + * Checks if the execution is in verbose mode. + * @return A boolean value indicating if the execution is in verbose mode. + */ + private static boolean isVerboseMode() { + return (Boolean) ConfigurationController. + getInstance().get("execution.verbose"); + } + + /** + * Displays the rule authors in the terminal. + * @param authors The list of authors. + */ + public static void printAuthors(List authors) { + StringBuilder line = new StringBuilder(); + line.append(authors.size() == 1 ? + messages.getMessage(Messages.INFO_LABEL_AUTHOR) + : messages.getMessage(Messages.INFO_LABEL_AUTHORS)); + String text = authors.isEmpty() ? + messages.getMessage(Messages.INFO_LABEL_NO_AUTHORS) + : CommonUtils.getCollectionElements( + CommonUtils.trimSpaces(authors), "", "", ", "); + line.append(" ").append(text); + wrapText(line.toString()); + } + + /** + * Displays the current conditional in the terminal. + * @param conditional The conditional object. + */ + public static void printConditional(Conditional conditional) { + if (conditional.getType() != Conditional.ConditionalType.NONE) { + StringBuilder line = new StringBuilder(); + line.append(messages.getMessage(Messages.INFO_LABEL_CONDITIONAL)); + line.append(" ("); + line.append(String.valueOf(conditional.getType())); + line.append(") ").append(conditional.getCondition()); + wrapText(line.toString()); + } + } + + /** + * Displays the file information in the terminal. + */ + public static void printFileInformation() { + File file = (File) ConfigurationController. + getInstance().get("execution.reference"); + String version = (String) ConfigurationController. + getInstance().get("application.version"); + String revision = (String) ConfigurationController. + getInstance().get("application.revision"); + String line = messages.getMessage( + Messages.INFO_DISPLAY_FILE_INFORMATION, + file.getName(), + CommonUtils.calculateFileSize(file), + CommonUtils.getLastModifiedInformation(file) + ); + logger.info(messages.getMessage( + Messages.LOG_INFO_WELCOME_MESSAGE, + version, + revision + )); + logger.info(displaySeparator()); + logger.info(String.format("::: arara @ %s", + getApplicationPath() + )); + logger.info(String.format("::: Java %s, %s", + CommonUtils.getSystemProperty("java.version", + "[unknown version]"), + CommonUtils.getSystemProperty("java.vendor", + "[unknown vendor]") + )); + logger.info(String.format("::: %s", + CommonUtils.getSystemProperty("java.home", + "[unknown location]") + )); + logger.info(String.format("::: %s, %s, %s", + CommonUtils.getSystemProperty("os.name", + "[unknown OS name]"), + CommonUtils.getSystemProperty("os.arch", + "[unknown OS arch]"), + CommonUtils.getSystemProperty("os.version", + "[unknown OS version]") + )); + logger.info(String.format("::: user.home @ %s", + CommonUtils.getSystemProperty("user.home", + "[unknown user's home directory]") + )); + logger.info(String.format("::: user.dir @ %s", + CommonUtils.getSystemProperty("user.dir", + "[unknown user's working directory]") + )); + logger.info(String.format("::: CF @ %s", + (String) ConfigurationController. + getInstance().get("execution.configuration.name") + )); + logger.info(displaySeparator()); + logger.info(line); + wrapText(line); + addNewLine(); + } + + /** + * Displays the elapsed time in the terminal. + */ + public static void printTime() { + if (ConfigurationController.getInstance().contains("display.time")) { + if ((ConfigurationController.getInstance().contains("display.line")) + || (ConfigurationController.getInstance(). + contains("display.exception"))) { + addNewLine(); + } + String text = messages.getMessage( + Messages.INFO_DISPLAY_EXECUTION_TIME, StopWatch.getTime()); + logger.info(text); + wrapText(text); + } + } + + /** + * Displays the application logo in the terminal. + */ + public static void printLogo() { + StringBuilder builder = new StringBuilder(); + builder.append(" __ _ _ __ __ _ _ __ __ _ ").append("\n"); + builder.append(" / _` | '__/ _` | '__/ _` |").append("\n"); + builder.append("| (_| | | | (_| | | | (_| |").append("\n"); + builder.append(" \\__,_|_| \\__,_|_| \\__,_|"); + System.out.println(builder.toString()); + addNewLine(); + } + + /** + * Adds a new line in the terminal. + */ + private static void addNewLine() { + System.out.println(); + } + + /** + * Displays a line containing details. + */ + private static void displayDetailsLine() { + String line = messages.getMessage( + Messages.INFO_LABEL_ON_DETAILS).concat(" "); + line = StringUtils.rightPad( + StringUtils.abbreviate(line, getWidth()), getWidth(), "-"); + System.out.println(line); + } + + /** + * Gets the output separator with the provided text. + * @param message The provided text. + * @return A string containing the output separator with the provided text. + */ + public static String displayOutputSeparator(String message) { + return StringUtils.center(" ".concat(message).concat(" "), + getWidth(), "-"); + } + + /** + * Gets the line separator. + * @return A string containing the line separator. + */ + public static String displaySeparator() { + return StringUtils.repeat("-", getWidth()); + } + + /** + * Gets the application path. + * @return A string containing the application path. + */ + private static String getApplicationPath() { + try { + return ConfigurationUtils.getApplicationPath(); + } + catch (AraraException ae) { + return "[unknown application path]"; + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/FileHandlingUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/FileHandlingUtils.java new file mode 100644 index 0000000000..9f56c92f5d --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/FileHandlingUtils.java @@ -0,0 +1,124 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.io.FileUtils; + +/** + * Implements file handling utilitary methods. + * + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class FileHandlingUtils { + + /** + * 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. + */ + public static boolean writeToFile(File file, String text, boolean append) { + try { + + // try to write the provided + // string to the file, with + // UTF-8 as encoding + FileUtils.writeStringToFile(file, text, "UTF-8", append); + return true; + + } catch (IOException nothandled) { + + // if something bad happens, + // gracefully fallback to + // reporting the failure + return 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. + */ + public static boolean writeToFile(File file, List lines, + boolean append) { + try { + + // try to write the provided + // string lists to the file, + // with UTF-8 as encoding + FileUtils.writeLines(file, "UTF-8", lines, append); + return true; + + } catch (IOException nothandled) { + + // if something bad happens, + // gracefully fallback to + // reporting the failure + return false; + } + } + + /** + * Reads the provided file into a list of strings. + * @param file The file. + * @return A list of strings. + */ + public static List readFromFile(File file) { + try { + + // returns the contents of + // the provided file as + // a list of strings + return FileUtils.readLines(file, "UTF-8"); + + } catch (IOException nothandled) { + + // if something bad happens, + // gracefully fallback to + // an empty file list + return new ArrayList(); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/FileSearchingUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/FileSearchingUtils.java new file mode 100644 index 0000000000..473674cbc5 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/FileSearchingUtils.java @@ -0,0 +1,117 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FalseFileFilter; +import org.apache.commons.io.filefilter.TrueFileFilter; +import org.apache.commons.io.filefilter.WildcardFileFilter; + +/** + * Implements file searching utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class 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. + */ + public static List listFilesByExtensions(File directory, + List extensions, boolean recursive) { + try { + + // convert the provided extension + // list to an extension array + String[] array = new String[extensions.size()]; + array = extensions.toArray(array); + + // return the result of the + // provided search + return new ArrayList( + FileUtils.listFiles(directory, array, recursive) + ); + } catch (Exception nothandled) { + + // if something bad happens, + // gracefully fallback to + // an empty file list + return new ArrayList(); + } + } + + /** + * 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. + */ + public static List listFilesByPatterns(File directory, + List patterns, boolean recursive) { + try { + + // return the result of the provided + // search, with the wildcard filter + // and a potential recursive search + return new ArrayList( + FileUtils.listFiles( + directory, + new WildcardFileFilter(patterns), + recursive + ? TrueFileFilter.INSTANCE + : FalseFileFilter.INSTANCE + ) + ); + } catch (Exception nothandled) { + + // if something bad happens, + // gracefully fallback to + // an empty file list + return new ArrayList(); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/InterpreterUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/InterpreterUtils.java new file mode 100644 index 0000000000..978cb07374 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/InterpreterUtils.java @@ -0,0 +1,257 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Argument; +import com.github.cereda.arara.model.Command; +import com.github.cereda.arara.model.Conditional; +import com.github.cereda.arara.model.Messages; +import com.github.cereda.arara.model.Rule; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; +import org.slf4j.Logger; +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 Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class InterpreterUtils { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + // get the logger context from a factory + private static final Logger logger = + LoggerFactory.getLogger(InterpreterUtils.class); + + /** + * Gets a list of all rule arguments. + * @param rule The provided rule. + * @return A list of strings containing all rule arguments. + */ + public static List getRuleArguments(Rule rule) { + Collection result = CollectionUtils.collect( + rule.getArguments(), new Transformer() { + public String transform(Argument input) { + return input.getIdentifier(); + } + }); + return new ArrayList(result); + } + + /** + * 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. + */ + public static boolean runPriorEvaluation(Conditional conditional) { + if (((Boolean) ConfigurationController.getInstance(). + get("execution.dryrun")) == true) { + return false; + } + switch (conditional.getType()) { + case IF: + case WHILE: + case UNLESS: + return true; + default: + return false; + } + } + + /** + * 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. + */ + public static int run(Object command) throws AraraException { + boolean verbose = (Boolean) ConfigurationController. + getInstance().get("execution.verbose"); + boolean timeout = (Boolean) ConfigurationController. + getInstance().get("execution.timeout"); + long value = (Long) ConfigurationController. + getInstance().get("execution.timeout.value"); + TimeUnit unit = (TimeUnit) ConfigurationController. + getInstance().get("execution.timeout.unit"); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + ProcessExecutor executor = new ProcessExecutor(); + if (CommonUtils.checkClass(Command.class, command)) { + executor = executor.command(((Command) command).getElements()); + if (((Command) command).hasWorkingDirectory()) { + executor = executor.directory( + ((Command) command).getWorkingDirectory() + ); + } + } else { + executor = executor.commandSplit((String) command); + } + if (timeout) { + if (value == 0) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_RUN_TIMEOUT_INVALID_RANGE + ) + ); + } + executor = executor.timeout(value, unit); + } + TeeOutputStream tee; + if (verbose) { + tee = new TeeOutputStream(System.out, buffer); + executor = executor.redirectInput(System.in); + } else { + tee = new TeeOutputStream(buffer); + } + executor = executor.redirectOutput(tee).redirectError(tee); + ShutdownHookProcessDestroyer hook = new ShutdownHookProcessDestroyer(); + executor = executor.addDestroyer(hook); + try { + int exit = executor.execute().getExitValue(); + 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 + ) + )); + return exit; + } catch (IOException ioexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_RUN_IO_EXCEPTION + ), + ioexception + ); + } catch (InterruptedException iexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_RUN_INTERRUPTED_EXCEPTION + ), + iexception + ); + } catch (InvalidExitValueException ievexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_RUN_INVALID_EXIT_VALUE_EXCEPTION + ), + ievexception + ); + } catch (TimeoutException texception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_RUN_TIMEOUT_EXCEPTION + ), + texception + ); + } catch (Exception exception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_RUN_GENERIC_EXCEPTION + ), + exception + ); + } + } + + /** + * 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. + */ + public static File buildRulePath(String name) throws AraraException { + @SuppressWarnings("unchecked") + List paths = (List) ConfigurationController. + getInstance().get("execution.rule.paths"); + for (String path : paths) { + File location = new 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. + */ + public static String construct(String path, String name) + throws AraraException { + name = name.concat(".yaml"); + File location = new File(path); + if (location.isAbsolute()) { + return CommonUtils.buildPath(path, name); + } else { + File reference = (File) ConfigurationController. + getInstance().get("execution.reference"); + String parent = CommonUtils.buildPath( + CommonUtils.getParentCanonicalPath(reference), path); + return CommonUtils.buildPath(parent, name); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/MessageUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/MessageUtils.java new file mode 100644 index 0000000000..52a5d095a8 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/MessageUtils.java @@ -0,0 +1,313 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.ConfigurationController; +import javax.swing.JOptionPane; +import javax.swing.UIManager; + +/** + * Implements utilitary methods for displaying messages. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class MessageUtils { + + // holds the default width for the + // message body, in pixels + private static final int WIDTH = 250; + + // let's start the UI manager and set + // the default look and feel to be as + // close as possible to the system + static { + + // get the current look and feel + String laf = (String) ConfigurationController. + getInstance().get("ui.lookandfeel"); + + // check if one is actually set + if (!laf.equals("none")) { + + // use a special keyword to indicate + // the use of a system look and feel + if (laf.equals("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 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. + */ + private static int normalizeIconType(int value) { + + // do the normalization according to the available + // icons in the underlying message implementation + switch (value) { + case 1: + value = JOptionPane.ERROR_MESSAGE; + break; + case 2: + value = JOptionPane.INFORMATION_MESSAGE; + break; + case 3: + value = JOptionPane.WARNING_MESSAGE; + break; + case 4: + value = JOptionPane.QUESTION_MESSAGE; + break; + default: + value = JOptionPane.PLAIN_MESSAGE; + break; + } + return value; + } + + /** + * 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 static int normalizeMessageWidth(int value) { + return (value > 0 ? value : 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. + */ + public static void showMessage(int width, int type, + String title, String text) { + + // effectively shows the message based + // on the provided parameters + JOptionPane.showMessageDialog( + null, + String.format( + "%s", + 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. + */ + public static void showMessage(int type, String title, String text) { + 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. + */ + public static int showOptions(int width, int type, String title, + String text, Object... buttons) { + + // returns the index of the selected button, + // zero if nothing is selected + return JOptionPane.showOptionDialog( + null, + String.format( + "%s", + 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. + */ + public static int showOptions(int type, String title, + String text, Object... buttons) { + 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. + */ + public static String showInput(int width, int type, + String title, String text) { + + // get the string from the + // input text, if any + String input = JOptionPane.showInputDialog( + null, + String.format( + "%s", + normalizeMessageWidth(width), + text + ), + title, + normalizeIconType(type) + ); + + // if the input is not null, that is, + // the user actually typed something + if (input != null) { + + // return the trimmed string + return input.trim(); + } + + // nothing was typed, so let's + // return an empty string + return ""; + } + + /** + * 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. + */ + public static String showInput(int type, String title, String text) { + 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. + */ + public static int showDropdown(int width, int type, String title, + String text, Object... elements) { + + // show the dropdown list and get + // the selected object, if any + Object index = JOptionPane.showInputDialog( + null, + String.format( + "%s", + 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) { + + // iterate through the array of elements + for (int i = 0; i < elements.length; i++) { + + // if the element is found, simply + // return the index plus 1, as zero + // corresponds to no selection at all + if (elements[i].equals(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. + */ + public static int showDropdown(int type, String title, + String text, Object... elements) { + return showDropdown(WIDTH, type, title, text, elements); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/Methods.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/Methods.java new file mode 100644 index 0000000000..15c6604934 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/Methods.java @@ -0,0 +1,1356 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.ConfigurationController; +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Command; +import com.github.cereda.arara.model.Messages; +import com.github.cereda.arara.model.Pair; +import com.github.cereda.arara.model.Session; +import com.github.cereda.arara.model.Trigger; +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Implements some auxiliary methods for runtime evaluation. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class Methods { + + // the language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + // the session controller + private static final Session session = new Session(); + + /** + * Adds the rule methods to the provided map. + * @param map The map. + */ + public static void addRuleMethods(Map map) { + addConditionalMethods(map); + try { + map.put("getOriginalFile", Methods.class.getMethod("getOriginalFile")); + map.put("getOriginalReference", Methods.class.getMethod("getOriginalReference")); + map.put("isEmpty", Methods.class.getMethod("isEmpty", String.class)); + map.put("isNotEmpty", Methods.class.getMethod("isNotEmpty", String.class)); + map.put("isEmpty", Methods.class.getMethod("isEmpty", String.class, Object.class)); + map.put("isNotEmpty", Methods.class.getMethod("isNotEmpty", String.class, Object.class)); + map.put("isEmpty", Methods.class.getMethod("isEmpty", String.class, Object.class, Object.class)); + map.put("isNotEmpty", Methods.class.getMethod("isNotEmpty", String.class, Object.class, Object.class)); + map.put("isTrue", Methods.class.getMethod("isTrue", String.class)); + map.put("isFalse", Methods.class.getMethod("isFalse", String.class)); + map.put("isTrue", Methods.class.getMethod("isTrue", String.class, Object.class)); + map.put("isFalse", Methods.class.getMethod("isFalse", String.class, Object.class)); + map.put("isTrue", Methods.class.getMethod("isTrue", String.class, Object.class, Object.class)); + map.put("isFalse", Methods.class.getMethod("isFalse", String.class, Object.class, Object.class)); + map.put("isTrue", Methods.class.getMethod("isTrue", String.class, Object.class, Object.class, Object.class)); + map.put("isFalse", Methods.class.getMethod("isFalse", String.class, Object.class, Object.class, Object.class)); + map.put("trimSpaces", Methods.class.getMethod("trimSpaces", String.class)); + map.put("isTrue", Methods.class.getMethod("isTrue", boolean.class, Object.class)); + map.put("isFalse", Methods.class.getMethod("isFalse", boolean.class, Object.class)); + map.put("isTrue", Methods.class.getMethod("isTrue", boolean.class, Object.class, Object.class)); + map.put("isFalse", Methods.class.getMethod("isFalse", boolean.class, Object.class, Object.class)); + map.put("getBasename", Methods.class.getMethod("getBasename", File.class)); + map.put("getBasename", Methods.class.getMethod("getBasename", String.class)); + map.put("getFullBasename", Methods.class.getMethod("getFullBasename", File.class)); + map.put("getFullBasename", Methods.class.getMethod("getFullBasename", String.class)); + map.put("getFiletype", Methods.class.getMethod("getFiletype", File.class)); + map.put("getFiletype", Methods.class.getMethod("getFiletype", String.class)); + map.put("throwError", Methods.class.getMethod("throwError", String.class)); + map.put("getSession", Methods.class.getMethod("getSession")); + map.put("isWindows", Methods.class.getMethod("isWindows")); + map.put("isLinux", Methods.class.getMethod("isLinux")); + map.put("isMac", Methods.class.getMethod("isMac")); + map.put("isUnix", Methods.class.getMethod("isUnix")); + map.put("isAIX", Methods.class.getMethod("isAIX")); + map.put("isIrix", Methods.class.getMethod("isIrix")); + map.put("isOS2", Methods.class.getMethod("isOS2")); + map.put("isSolaris", Methods.class.getMethod("isSolaris")); + map.put("isCygwin", Methods.class.getMethod("isCygwin")); + map.put("isWindows", Methods.class.getMethod("isWindows", Object.class, Object.class)); + map.put("isLinux", Methods.class.getMethod("isLinux", Object.class, Object.class)); + map.put("isMac", Methods.class.getMethod("isMac", Object.class, Object.class)); + map.put("isUnix", Methods.class.getMethod("isUnix", Object.class, Object.class)); + map.put("isAIX", Methods.class.getMethod("isAIX", Object.class, Object.class)); + map.put("isIrix", Methods.class.getMethod("isIrix", Object.class, Object.class)); + map.put("isOS2", Methods.class.getMethod("isOS2", Object.class, Object.class)); + map.put("isSolaris", Methods.class.getMethod("isSolaris", Object.class, Object.class)); + map.put("isCygwin", Methods.class.getMethod("isCygwin", Object.class, Object.class)); + map.put("replicatePattern", Methods.class.getMethod("replicatePattern", String.class, List.class)); + map.put("buildString", Methods.class.getMethod("buildString", Object[].class)); + map.put("addQuotes", Methods.class.getMethod("addQuotes", Object.class)); + map.put("getCommand", Methods.class.getMethod("getCommand", List.class)); + map.put("getCommand", Methods.class.getMethod("getCommand", Object[].class)); + map.put("getTrigger", Methods.class.getMethod("getTrigger", String.class)); + map.put("getTrigger", Methods.class.getMethod("getTrigger", String.class, Object[].class)); + map.put("checkClass", Methods.class.getMethod("checkClass", Class.class, Object.class)); + map.put("isString", Methods.class.getMethod("isString", Object.class)); + map.put("isList", Methods.class.getMethod("isList", Object.class)); + map.put("isMap", Methods.class.getMethod("isMap", Object.class)); + map.put("isBoolean", Methods.class.getMethod("isBoolean", Object.class)); + map.put("isVerboseMode", Methods.class.getMethod("isVerboseMode")); + map.put("showMessage", Methods.class.getMethod("showMessage", int.class, String.class, String.class)); + map.put("showMessage", Methods.class.getMethod("showMessage", int.class, int.class, String.class, String.class)); + map.put("isOnPath", Methods.class.getMethod("isOnPath", String.class)); + map.put("unsafelyExecuteSystemCommand", Methods.class.getMethod("unsafelyExecuteSystemCommand", Command.class)); + map.put("mergeVelocityTemplate", Methods.class.getMethod("mergeVelocityTemplate", File.class, File.class, Map.class)); + map.put("getCommandWithWorkingDirectory", Methods.class.getMethod("getCommandWithWorkingDirectory", String.class, List.class)); + map.put("getCommandWithWorkingDirectory", Methods.class.getMethod("getCommandWithWorkingDirectory", String.class, Object[].class)); + map.put("getCommandWithWorkingDirectory", Methods.class.getMethod("getCommandWithWorkingDirectory", File.class, List.class)); + map.put("getCommandWithWorkingDirectory", Methods.class.getMethod("getCommandWithWorkingDirectory", File.class, Object[].class)); + map.put("listFilesByExtensions", Methods.class.getMethod("listFilesByExtensions", File.class, List.class, boolean.class)); + map.put("listFilesByExtensions", Methods.class.getMethod("listFilesByExtensions", String.class, List.class, boolean.class)); + map.put("listFilesByPatterns", Methods.class.getMethod("listFilesByPatterns", File.class, List.class, boolean.class)); + map.put("listFilesByPatterns", Methods.class.getMethod("listFilesByPatterns", String.class, List.class, boolean.class)); + map.put("writeToFile", Methods.class.getMethod("writeToFile", File.class, String.class, boolean.class)); + map.put("writeToFile", Methods.class.getMethod("writeToFile", File.class, List.class, boolean.class)); + map.put("writeToFile", Methods.class.getMethod("writeToFile", String.class, String.class, boolean.class)); + map.put("writeToFile", Methods.class.getMethod("writeToFile", String.class, List.class, boolean.class)); + map.put("readFromFile", Methods.class.getMethod("readFromFile", File.class)); + map.put("readFromFile", Methods.class.getMethod("readFromFile", String.class)); + } catch (Exception exception) { + // quack, quack, quack + } + } + + /** + * Adds conditional methods to the provided map. + * @param map The map. + */ + public static void addConditionalMethods(Map map) { + try { + map.put("exists", Methods.class.getMethod("exists", String.class)); + map.put("exists", Methods.class.getMethod("exists", File.class)); + map.put("missing", Methods.class.getMethod("missing", String.class)); + map.put("missing", Methods.class.getMethod("missing", File.class)); + map.put("changed", Methods.class.getMethod("changed", String.class)); + map.put("changed", Methods.class.getMethod("changed", File.class)); + map.put("unchanged", Methods.class.getMethod("unchanged", String.class)); + map.put("unchanged", Methods.class.getMethod("unchanged", File.class)); + map.put("found", Methods.class.getMethod("found", String.class, String.class)); + map.put("found", Methods.class.getMethod("found", File.class, String.class)); + map.put("toFile", Methods.class.getMethod("toFile", String.class)); + map.put("showDropdown", Methods.class.getMethod("showDropdown", int.class, String.class, String.class, Object[].class)); + map.put("showDropdown", Methods.class.getMethod("showDropdown", int.class, int.class, String.class, String.class, Object[].class)); + map.put("showInput", Methods.class.getMethod("showInput", int.class, String.class, String.class)); + map.put("showInput", Methods.class.getMethod("showInput", int.class, int.class, String.class, String.class)); + map.put("showOptions", Methods.class.getMethod("showOptions", int.class, String.class, String.class, Object[].class)); + map.put("showOptions", Methods.class.getMethod("showOptions", int.class, int.class, String.class, String.class, Object[].class)); + map.put("currentFile", Methods.class.getMethod("currentFile")); + map.put("loadClass", Methods.class.getMethod("loadClass", File.class, String.class)); + map.put("loadClass", Methods.class.getMethod("loadClass", String.class, String.class)); + map.put("loadObject", Methods.class.getMethod("loadObject", File.class, String.class)); + map.put("loadObject", Methods.class.getMethod("loadObject", String.class, String.class)); + } catch (Exception exception) { + // quack, quack, quack + } + } + + /** + * Gets the original file. + * @return The original file. + */ + public static String getOriginalFile() { + File file = (File) ConfigurationController.getInstance().get("execution.reference"); + return file.getName(); + } + + /** + * Gets the original reference. + * @return The original reference. + */ + public static File getOriginalReference() { + return (File) ConfigurationController.getInstance().get("execution.reference"); + } + + /** + * Checks if the string is empty. + * @param string The string. + * @return A boolean value. + */ + public static boolean isEmpty(String string) { + return CommonUtils.checkEmptyString(string); + } + + /** + * Checks if the string is not empty. + * @param string The string. + * @return A boolean value. + */ + public static boolean isNotEmpty(String string) { + return !isEmpty(string); + } + + /** + * Checks if the string is empty. + * @param string The string. + * @param yes Object to return if true. + * @return An object or empty string. + */ + public static Object isEmpty(String string, Object yes) { + return isEmpty(string) ? yes : ""; + } + + /** + * Checks if the string is not empty. + * @param string The string. + * @param yes Object to return if true. + * @return An object or empty string. + */ + public static Object isNotEmpty(String string, Object yes) { + return isNotEmpty(string) ? yes : ""; + } + + /** + * Checks if the string is empty. + * @param string The string. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + */ + public static Object isEmpty(String string, Object yes, Object no) { + return isEmpty(string) ? yes : no; + } + + /** + * Checks if the string is not empty. + * @param string The string. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + */ + public static Object isNotEmpty(String string, Object yes, Object no) { + return isNotEmpty(string) ? yes : no; + } + + /** + * Checks if the string holds a true value. + * @param string The string. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isTrue(String string) throws AraraException { + return isEmpty(string) ? false : CommonUtils.checkBoolean(string); + } + + /** + * Checks if the string holds a false value. + * @param string The string. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isFalse(String string) throws AraraException { + return isEmpty(string) ? false : !CommonUtils.checkBoolean(string); + } + + /** + * Checks if the string holds a true value. + * @param string The string. + * @param yes Object to return if true. + * @return An object or an empty string. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isTrue(String string, Object yes) + throws AraraException { + return isTrue(string) ? yes : ""; + } + + /** + * Checks if the string holds a false value. + * @param string The string. + * @param yes Object to return if true. + * @return An object or an empty string. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isFalse(String string, Object yes) + throws AraraException { + return (isFalse(string) ? yes : ""); + } + + /** + * Checks if the string holds a true value. + * @param string The string. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isTrue(String string, Object yes, Object no) + throws AraraException { + return (isTrue(string) ? yes : no); + } + + /** + * Checks if the string holds a false value. + * @param string The string. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isFalse(String string, Object yes, Object no) + throws AraraException { + return (isFalse(string) ? yes : no); + } + + /** + * Checks if the string holds a true value. + * @param string The string. + * @param yes Object to return if true. + * @param no Object to return if false. + * @param fallback Object to return if string is empty. + * @return One of the three options. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isTrue(String string, Object yes, Object no, + Object fallback) throws AraraException { + return isEmpty(string) ? fallback : (isTrue(string) ? yes : no); + } + + /** + * Checks if the string holds a false value. + * @param string The string. + * @param yes Object to return if true. + * @param no Object to return if false. + * @param fallback Object to return if string is empty. + * @return One of the three options. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isFalse(String string, Object yes, Object no, + Object fallback) throws AraraException { + return isEmpty(string) ? fallback : (isFalse(string) ? yes : no); + } + + /** + * Trim spaces from the string. + * @param string The string. + * @return A trimmed string. + */ + public static String trimSpaces(String string) { + return string.trim(); + } + + /** + * Checks if the expression resolves to true. + * @param value The expression. + * @param yes Object to return if true. + * @return An object or an empty string. + */ + public static Object isTrue(boolean value, Object yes) { + return value ? yes : ""; + } + + /** + * Checks if the expression resolves to false. + * @param value The expression. + * @param yes Object to return if true. + * @return An object or an empty string. + */ + public static Object isFalse(boolean value, Object yes) { + return !value ? yes : ""; + } + + /** + * Checks if the expression resolves to true. + * @param value The expression. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + */ + public static Object isTrue(boolean value, Object yes, Object no) { + return value ? yes : no; + } + + /** + * Checks if the expression resolves to false. + * @param value The expression. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + */ + public static Object isFalse(boolean value, Object yes, Object no) { + return !value ? yes : no; + } + + /** + * Gets the basename. + * @param file The file. + * @return The basename of the provided file. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static String getBasename(File file) throws AraraException { + if (file.isFile()) { + return CommonUtils.getBasename(file); + } else { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_BASENAME_NOT_A_FILE, + file.getName() + ) + ) + ); + } + } + + /** + * Gets the basename. + * @param filename The string. + * @return The basename. + */ + public static String getBasename(String filename) { + return CommonUtils.getBasename(filename); + } + + /** + * Gets the file type. + * @param file The provided file. + * @return The file type. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static String getFiletype(File file) throws AraraException { + if (file.isFile()) { + return CommonUtils.getFiletype(file); + } else { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_FILETYPE_NOT_A_FILE, + file.getName() + ) + ) + ); + } + } + + /** + * Gets the file type. + * @param filename The provided string. + * @return The file type. + */ + public static String getFiletype(String filename) { + return CommonUtils.getFiletype(filename); + } + + /** + * Replicates the pattern to each element of a list. + * @param pattern The pattern. + * @param values The list. + * @return A list of strings containing the pattern applied to the list. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static List replicatePattern(String pattern, + List values) throws AraraException { + return CommonUtils.replicateList(pattern, values); + } + + /** + * Throws an exception. + * @param text The text to be thrown as the exception message. + * @throws AraraException The exception to be thrown by this method. + */ + public static void throwError(String text) throws AraraException { + throw new AraraException(text); + } + + /** + * Gets the session. + * @return The session. + */ + public static Session getSession() { + return session; + } + + /** + * Checks if Windows is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isWindows() throws AraraException { + return CommonUtils.checkOS("windows"); + } + + /** + * Checks if we are inside a Cygwin environment. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isCygwin() throws AraraException { + return CommonUtils.checkOS("cygwin"); + } + + /** + * Checks if Linux is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isLinux() throws AraraException { + return CommonUtils.checkOS("linux"); + } + + /** + * Checks if Mac is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isMac() throws AraraException { + return CommonUtils.checkOS("mac"); + } + + /** + * Checks if Unix is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isUnix() throws AraraException { + return CommonUtils.checkOS("unix"); + } + + /** + * Checks if AIX is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isAIX() throws AraraException { + return CommonUtils.checkOS("aix"); + } + + /** + * Checks if Irix is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isIrix() throws AraraException { + return CommonUtils.checkOS("irix"); + } + + /** + * Checks if OS2 is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isOS2() throws AraraException { + return CommonUtils.checkOS("os2"); + } + + /** + * Checks if Solaris is the underlying operating system. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean isSolaris() throws AraraException { + return CommonUtils.checkOS("solaris"); + } + + /** + * Checks if Windows is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isWindows(Object yes, Object no) + throws AraraException { + return CommonUtils.checkOS("windows") ? yes : no; + } + + /** + * Checks if we are inside a Cygwin environment. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isCygwin(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("cygwin") ? yes : no; + } + + /** + * Checks if Linux is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isLinux(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("linux") ? yes : no; + } + + /** + * Checks if Mac is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isMac(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("mac") ? yes : no; + } + + /** + * Checks if Unix is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isUnix(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("unix") ? yes : no; + } + + /** + * Checks if AIX is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isAIX(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("aix") ? yes : no; + } + + /** + * Checks if Irix is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isIrix(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("irix") ? yes : no; + } + + /** + * Checks if OS2 is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isOS2(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("os2") ? yes : no; + } + + /** + * Checks if Solaris is the underlying operating system. + * @param yes Object to return if true. + * @param no Object to return if false. + * @return One of the two objects. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static Object isSolaris(Object yes, Object no) throws AraraException { + return CommonUtils.checkOS("solaris") ? yes : no; + } + + /** + * Checks if the file exists according to its extension. + * @param extension The extension. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean exists(String extension) throws AraraException { + return CommonUtils.exists(extension); + } + + /** + * Checks if the file is missing according to its extension. + * @param extension The extension. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean missing(String extension) throws AraraException { + return !exists(extension); + } + + /** + * Checks if the file has changed, according to its extension. + * @param extension The extension. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean changed(String extension) throws AraraException { + return CommonUtils.hasChanged(extension); + } + + /** + * Checks if the file is unchanged according to its extension. + * @param extension The extension. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean unchanged(String extension) throws AraraException { + return !changed(extension); + } + + /** + * Checks if the file exists. + * @param filename The file. + * @return A boolean value. + */ + public static boolean exists(File filename) { + return CommonUtils.exists(filename); + } + + /** + * Checks if the file is missing. + * @param filename The file. + * @return A boolean value. + */ + public static boolean missing(File filename) { + return !exists(filename); + } + + /** + * Checks if the file has changed. + * @param filename The file. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean changed(File filename) throws AraraException { + return CommonUtils.hasChanged(filename); + } + + /** + * Checks if the file is unchanged. + * @param filename The file. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean unchanged(File filename) throws AraraException { + return !changed(filename); + } + + /** + * Build a string based on an array of objects. + * @param objects Array of objects. + * @return A string built from the array. + */ + public static String buildString(Object... objects) { + return CommonUtils.generateString(objects); + } + + /** + * Encloses the provided object into double quotes. + * @param object The object. + * @return The object enclosed in double quotes. + */ + public static String addQuotes(Object object) { + return CommonUtils.addQuotes(object); + } + + /** + * Checks if the file contains the regex, based on its extension. + * @param extension The extension. + * @param regex The regex. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean found(String extension, String regex) + throws AraraException { + return CommonUtils.checkRegex(extension, regex); + } + + /** + * Checks if the file contains the provided regex. + * @param file The file. + * @param regex The regex. + * @return A boolean value. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static boolean found(File file, String regex) + throws AraraException { + return CommonUtils.checkRegex(file, regex); + } + + /** + * Gets the command based on a list of strings. + * @param elements The list of strings. + * @return A command. + */ + public static Command getCommand(List elements) { + return new Command(elements); + } + + /** + * Gets the command based on an array of objects. + * @param elements Array of objects. + * @return A command. + */ + public static Command getCommand(Object... elements) { + return new Command(elements); + } + + /** + * Gets the command based on an array of objects and with the provided + * working directory as string. + * @param path String path representing the working directory. + * @param elements Array of elements. + * @return A command. + */ + public static Command getCommandWithWorkingDirectory(String path, + Object... elements) { + Command command = new Command(elements); + command.setWorkingDirectory(new File(path)); + return command; + } + + /** + * Gets the command based on an array of objects and with the provided + * working directory as file. + * @param file File representing the working directory. + * @param elements Array of elements. + * @return A command. + */ + public static Command getCommandWithWorkingDirectory(File file, + Object... elements) { + Command command = new Command(elements); + command.setWorkingDirectory(file); + return command; + } + + /** + * Gets the command based on a list of strings and with the provided + * working directory as string. + * @param path String path representing the working directory. + * @param elements List of strings. + * @return A command. + */ + public static Command getCommandWithWorkingDirectory(String path, + List elements) { + Command command = new Command(elements); + command.setWorkingDirectory(new File(path)); + return command; + } + + /** + * Gets the command based on a list of strings and with the provided + * working directory as file. + * @param file File representing the working directory. + * @param elements List of strings. + * @return A command. + */ + public static Command getCommandWithWorkingDirectory(File file, + List elements) { + Command command = new Command(elements); + command.setWorkingDirectory(file); + return command; + } + + /** + * Gets the trigger. + * @param action The action name. + * @return The trigger. + */ + public static Trigger getTrigger(String action) { + return new Trigger(action, null); + } + + /** + * Gets the trigger. + * @param action The action name. + * @param parameters The trigger parameters. + * @return A trigger. + */ + public static Trigger getTrigger(String action, Object... parameters) { + return new Trigger(action, Arrays.asList(parameters)); + } + + /** + * Checks if the object is an intance of the provided class. + * @param clazz The class. + * @param object The object. + * @return A boolean value. + */ + public static boolean checkClass(Class clazz, Object object) { + return CommonUtils.checkClass(clazz, object); + } + + /** + * Checks if the object is a string. + * @param object The object. + * @return A boolean value. + */ + public static boolean isString(Object object) { + return checkClass(String.class, object); + } + + /** + * Checks if the object is a list. + * @param object The object. + * @return A boolean value. + */ + public static boolean isList(Object object) { + return checkClass(List.class, object); + } + + /** + * Checks if the object is a map. + * @param object The object. + * @return A boolean value. + */ + public static boolean isMap(Object object) { + return checkClass(Map.class, object); + } + + /** + * Checks if the object is a boolean. + * @param object The object. + * @return A boolean value. + */ + public static boolean isBoolean(Object object) { + return checkClass(Boolean.class, object); + } + + /** + * Checks if the execution is in verbose mode. + * @return A boolean value indicating if the execution is in verbose mode. + */ + public static boolean isVerboseMode() { + return (Boolean) ConfigurationController. + getInstance().get("execution.verbose"); + } + + /** + * Returns a file object based on the provided name. + * @param name The file name. + * @return A file object. + */ + public static File toFile(String name) { + return new File(name); + } + + /** + * 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. + */ + public static void showMessage(int width, int type, + String title, String text) { + MessageUtils.showMessage(width, type, title, text); + } + + /** + * 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. + */ + public static void showMessage(int type, String title, String text) { + MessageUtils.showMessage(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. + */ + public static int showOptions(int width, int type, String title, + String text, Object... buttons) { + return MessageUtils.showOptions(width, type, title, text, buttons); + } + + /** + * 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. + */ + public static int showOptions(int type, String title, + String text, Object... buttons) { + return MessageUtils.showOptions(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. + */ + public static String showInput(int width, int type, + String title, String text) { + return MessageUtils.showInput(width, type, title, text); + } + + /** + * 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. + */ + public static String showInput(int type, String title, String text) { + return MessageUtils.showInput(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. + */ + public static int showDropdown(int width, int type, String title, + String text, Object... elements) { + return MessageUtils.showDropdown(width, type, title, text, elements); + } + + /** + * 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. + */ + public static int showDropdown(int type, String title, + String text, Object... elements) { + return MessageUtils.showDropdown(type, title, text, elements); + } + + /** + * Checks if the provided command name is reachable from the system path. + * @param command A string representing the command. + * @return A logic value. + */ + public static boolean isOnPath(String command) { + return CommonUtils.isOnPath(command); + } + + /** + * Gets the full basename. + * @param file The file. + * @return The full basename of the provided file. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static String getFullBasename(File file) throws AraraException { + if (file.isFile()) { + return CommonUtils.getFullBasename(file); + } else { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_BASENAME_NOT_A_FILE, + file.getName() + ) + ) + ); + } + } + + /** + * Gets the full basename. + * @param name The string. + * @return The full basename. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static String getFullBasename(String name) throws AraraException { + return getFullBasename(new File(name)); + } + + /** + * Unsafely 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. + */ + public static Pair + unsafelyExecuteSystemCommand(Command command) { + return UnsafeUtils.executeSystemCommand(command); + } + + /** + * Merges the provided template with a context map and writes the result in + * an output file. This method relies on Apache Velocity. + * @param input The input file. + * @param output The output file. + * @param map The context map. + * @throws AraraException Something wrong happened, to be caught in the + * higher levels. + */ + public static void mergeVelocityTemplate(File input, File output, + Map map) throws AraraException { + VelocityUtils.mergeVelocityTemplate(input, output, map); + } + + /** + * Gets the file reference for the current directive. It is important to + * observe that version 4.0 of arara replicates the directive when 'files' + * is detected amongst the parameters, so each instance will have a + * different reference. + * @return A file reference for the current directive. + */ + public static File currentFile() { + return (File) ConfigurationController.getInstance(). + get("execution.directive.reference"); + } + + /** + * 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. + */ + public static Pair loadClass(File file, String name) { + return ClassLoadingUtils.loadClass(file, name); + } + + /** + * Loads a class from the provided string reference, representing a file. + * @param ref String reference representing a file. + * @param name The canonical name of the class. + * @return A pair representing the status and the class. + */ + public static Pair loadClass(String ref, String name) { + return ClassLoadingUtils.loadClass(new File(ref), name); + } + + /** + * 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. + */ + public static Pair loadObject(File file, String name) { + return ClassLoadingUtils.loadObject(file, name); + } + + /** + * Loads a class from the provided string reference, instantiating it. + * @param ref String reference representing a file. + * @param name The canonical name of the class. + * @return A pair representing the status and the class object. + */ + public static Pair loadObject(String ref, String name) { + return ClassLoadingUtils.loadObject(new File(ref), name); + } + + /** + * 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. + */ + public static List listFilesByExtensions(File directory, + List extensions, boolean recursive) { + return FileSearchingUtils.listFilesByExtensions( + directory, + extensions, + recursive + ); + } + + /** + * List all files from the provided string path according to the list of + * extensions. The leading dot must be omitted, unless it is part of the + * extension. + * @param path The provided path as plain string. + * @param extensions The list of extensions. + * @param recursive A flag indicating whether the search is recursive. + * @return A list of files. + */ + public static List listFilesByExtensions(String path, + List extensions, boolean recursive) { + return FileSearchingUtils.listFilesByExtensions( + new File(path), + extensions, + recursive + ); + } + + /** + * 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. + */ + public static List listFilesByPatterns(File directory, + List patterns, boolean recursive) { + return FileSearchingUtils.listFilesByPatterns( + directory, + patterns, + recursive + ); + } + + /** + * List all files from the provided path matching the list of file + * name patterns. Such list can contain wildcards. + * @param path The provided path as plain string. + * @param patterns The list of file name patterns. + * @param recursive A flag indicating whether the search is recursive. + * @return A list of files. + */ + public static List listFilesByPatterns(String path, + List patterns, boolean recursive) { + return FileSearchingUtils.listFilesByPatterns( + new File(path), + patterns, + recursive + ); + } + + /** + * 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. + */ + public static boolean writeToFile(File file, String text, boolean append) { + return FileHandlingUtils.writeToFile(file, text, append); + } + + /** + * Writes the string to a file, using UTF-8 as default encoding. + * @param path The path. + * @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. + */ + public static boolean writeToFile(String path, String text, + boolean append) { + return FileHandlingUtils.writeToFile(new File(path), text, append); + } + + /** + * 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. + */ + public static boolean writeToFile(File file, List lines, + boolean append) { + return FileHandlingUtils.writeToFile(file, lines, append); + } + + /** + * Writes the string list to a file, using UTF-8 as default encoding. + * @param path The path. + * @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. + */ + public static boolean writeToFile(String path, List lines, + boolean append) { + return FileHandlingUtils.writeToFile(new File(path), lines, append); + } + + /** + * Reads the provided file into a list of strings. + * @param file The file. + * @return A list of strings. + */ + public static List readFromFile(File file) { + return FileHandlingUtils.readFromFile(file); + } + + /** + * Reads the provided file into a list of strings. + * @param path The path. + * @return A list of strings. + */ + public static List readFromFile(String path) { + return FileHandlingUtils.readFromFile(new File(path)); + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/RuleUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/RuleUtils.java new file mode 100644 index 0000000000..d855344762 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/RuleUtils.java @@ -0,0 +1,244 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Argument; +import com.github.cereda.arara.model.Messages; +import com.github.cereda.arara.model.RuleCommand; +import com.github.cereda.arara.model.Rule; +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Predicate; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.error.MarkedYAMLException; +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.representer.Representer; + +/** + * Implements rule utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class RuleUtils { + + // the application messages obtained from the + // language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * 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. + */ + public static Rule parseRule(File file, String identifier) + throws AraraException { + Representer representer = new Representer(); + representer.addClassTag(Rule.class, new Tag("!config")); + Yaml yaml = new Yaml(new Constructor(Rule.class), representer); + Rule rule = null; + try { + rule = yaml.loadAs(new FileReader(file), Rule.class); + } catch (MarkedYAMLException yamlException) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_PARSERULE_INVALID_YAML + ) + ), + yamlException + ); + } catch (Exception exception) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_PARSERULE_GENERIC_ERROR + ) + ) + ); + } + 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. + */ + private static void validateHeader(Rule rule, String identifier) + throws AraraException { + if (rule.getIdentifier() != null) { + if (!rule.getIdentifier().equals(identifier)) { + throw new AraraException(CommonUtils.getRuleErrorHeader(). + concat( + messages.getMessage( + Messages.ERROR_VALIDATEHEADER_WRONG_IDENTIFIER, + rule.getIdentifier(), + identifier + ) + ) + ); + } + } else { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_VALIDATEHEADER_NULL_ID + ) + ) + ); + } + if (rule.getName() == null) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + 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. + */ + private static void validateBody(Rule rule) throws AraraException { + if (rule.getCommands() == null) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_VALIDATEBODY_NULL_COMMANDS_LIST + ) + ) + ); + } else { + if (CollectionUtils.exists(rule.getCommands(), + new Predicate() { + public boolean evaluate(RuleCommand command) { + return (command.getCommand() == null); + } + })) { + throw new AraraException(CommonUtils.getRuleErrorHeader(). + concat( + messages.getMessage( + Messages.ERROR_VALIDATEBODY_NULL_COMMAND + ) + ) + ); + } + } + if (rule.getArguments() == null) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_VALIDATEBODY_ARGUMENTS_LIST + ) + ) + ); + } else { + String[] keywords = new String[]{"file", "files", "reference"}; + + List arguments = new ArrayList(); + for (Argument argument : rule.getArguments()) { + if (argument.getIdentifier() != null) { + if ((argument.getFlag() != null) || + (argument.getDefault() != null)) { + arguments.add(argument.getIdentifier()); + } else { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_VALIDATEBODY_MISSING_KEYS + ) + ) + ); + } + } else { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_VALIDATEBODY_NULL_ARGUMENT_ID + ) + ) + ); + } + } + + for (String keyword : keywords) { + if (arguments.contains(keyword)) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_VALIDATEBODY_ARGUMENT_ID_IS_RESERVED, + keyword + ) + ) + ); + } + } + + int expected = arguments.size(); + int found = (new HashSet(arguments)).size(); + if (expected != found) { + throw new AraraException( + CommonUtils.getRuleErrorHeader().concat( + messages.getMessage( + Messages.ERROR_VALIDATEBODY_DUPLICATE_ARGUMENT_IDENTIFIERS + ) + ) + ); + } + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/TeeOutputStream.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/TeeOutputStream.java new file mode 100644 index 0000000000..65d3d58ffb --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/TeeOutputStream.java @@ -0,0 +1,109 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import java.io.IOException; +import java.io.OutputStream; +import org.apache.commons.io.IOUtils; + +/** + * Implements a stream splitter. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class TeeOutputStream extends OutputStream { + + // an array of streams in which + // an object of this class will + // split data + private final OutputStream[] streams; + + /** + * Constructor. + * @param outputStreams An array of output streams. + */ + public TeeOutputStream(OutputStream... outputStreams) { + streams = outputStreams; + } + + /** + * Writes the provided integer to each stream. + * @param b The provided integer + * @throws IOException An IO exception. + */ + @Override + public void write(int b) throws IOException { + for (OutputStream ostream : streams) { + ostream.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. + */ + @Override + public void write(byte[] b, int offset, int length) throws IOException { + for (OutputStream ostream : streams) { + ostream.write(b, offset, length); + } + } + + /** + * Flushes every stream. + * @throws IOException An IO exception. + */ + @Override + public void flush() throws IOException { + for (OutputStream ostream : streams) { + ostream.flush(); + } + } + + /** + * Closes every stream silently. + */ + @Override + public void close() { + for (OutputStream ostream : streams) { + IOUtils.closeQuietly(ostream); + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/UnsafeUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/UnsafeUtils.java new file mode 100644 index 0000000000..b989e074c8 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/UnsafeUtils.java @@ -0,0 +1,87 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.model.Command; +import com.github.cereda.arara.model.Pair; +import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.ProcessResult; + +/** + * Implements unsafe utilitary methods. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class UnsafeUtils { + + /** + * 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. + */ + public static Pair executeSystemCommand(Command command) { + + try { + + // create a process result with the provided + // command, capturing the output + ProcessResult result = new ProcessExecutor( + command.getElements() + ).readOutput(true).execute(); + + // return the pair containing the exit status + // and the output string as UTF-8 + return new Pair( + result.getExitValue(), + result.outputUTF8() + ); + + } catch (Exception exception) { + + // quack, quack, do nothing, just + // return a default error code + + // if something goes wrong, the default + // error branch returns an exit status of + // -99 and an empty string + return new Pair(-99, ""); + + } + } + +} diff --git a/support/arara/source/src/main/java/com/github/cereda/arara/utils/VelocityUtils.java b/support/arara/source/src/main/java/com/github/cereda/arara/utils/VelocityUtils.java new file mode 100644 index 0000000000..2040b671b7 --- /dev/null +++ b/support/arara/source/src/main/java/com/github/cereda/arara/utils/VelocityUtils.java @@ -0,0 +1,157 @@ +/** + * Arara, the cool TeX automation tool + * Copyright (c) 2012 -- 2018, Paulo Roberto Massa Cereda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the project's author nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.github.cereda.arara.utils; + +import com.github.cereda.arara.controller.LanguageController; +import com.github.cereda.arara.model.AraraException; +import com.github.cereda.arara.model.Messages; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Map; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.RuntimeConstants; + +/** + * Implements the template merging from Apache Velocity. + * @author Paulo Roberto Massa Cereda + * @version 4.0 + * @since 4.0 + */ +public class VelocityUtils { + + // the language controller + private static final LanguageController messages = + LanguageController.getInstance(); + + /** + * Merges the provided template with the context map and writes the result + * in an output file. The operation relies on the Apache Velocity project. + * @param input The input file. + * @param output The output file. + * @param map The context map. + * @throws AraraException Something terribly wrong happened, to be caught + * in the higher levels. + */ + public static void mergeVelocityTemplate(File input, File output, + Map map) throws AraraException { + + // let us try + try { + + // create the template engine instance + VelocityEngine engine = new VelocityEngine(); + + // use the resource path trick: set the default + // location to the input file's parent directory, + // so our file is easily located + engine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, + input.getCanonicalFile().getParent()); + + // set the logging feature of Apache Velocity to + // register the occurrences in a null provider + // (we do not want unnecessary verbose output) + engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, + "org.apache.velocity.runtime.log.NullLogSystem"); + + // init the engine with the + // provided settings + engine.init(); + + // create a context for Apache Velocity, + // based on the provided map + VelocityContext context = new VelocityContext(map); + + // get the template from the engine and + // read it as an UTF-8 file + Template template = engine.getTemplate(input.getName(), "UTF-8"); + + // create an output stream from + // the file output reference + FileOutputStream stream = new FileOutputStream(output); + + // create a writer based on the + // previously created stream + Writer writer = new OutputStreamWriter(stream, "UTF-8"); + + // merge the context map with the + // template file and write the result + // to the output stream writer + template.merge(context, writer); + + // close both writer + // and output stream + writer.close(); + stream.close(); + + } catch(ResourceNotFoundException rnfexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VELOCITY_FILE_NOT_FOUND + ), + rnfexception + ); + } catch(ParseErrorException peexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VELOCITY_PARSE_EXCEPTION + ), + peexception + ); + } catch(MethodInvocationException miexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VELOCITY_METHOD_INVOCATION_EXCEPTION + ), + miexception + ); + } catch (IOException ioexception) { + throw new AraraException( + messages.getMessage( + Messages.ERROR_VELOCITY_FILE_NOT_FOUND + ), + ioexception + ); + } + + } + +} -- cgit v1.2.3