// SPDX-License-Identifier: BSD-3-Clause package org.islandoftex.arara.filehandling import java.io.File import java.io.FileFilter import java.nio.file.FileSystems import org.islandoftex.arara.Arara import org.islandoftex.arara.configuration.AraraSpec import org.islandoftex.arara.localization.LanguageController import org.islandoftex.arara.localization.Messages import org.islandoftex.arara.model.AraraException import org.islandoftex.arara.utils.CommonUtils /** * Implements file searching utilitary methods. * * @author Island of TeX * @version 5.0 * @since 4.0 */ object FileSearchingUtils { /** * List all files from the provided directory according to the list of * extensions. The leading dot must be omitted, unless it is part of the * extension. * @param directory The provided directory. * @param extensions The list of extensions. * @param recursive A flag indicating whether the search is recursive. * @return A list of files. */ fun listFilesByExtensions( directory: File, extensions: List, recursive: Boolean ): List = try { // return the result of the // provided search if (recursive) directory.walkTopDown().asSequence() .filter { !it.isDirectory } .filter { extensions.contains(it.extension) } .toList() else directory.listFiles( FileFilter { extensions.contains(it.extension) })!! .toList() } catch (_: Exception) { // if something bad happens, // gracefully fallback to // an empty file list listOf() } /** * List all files from the provided directory matching the list of file * name patterns. Such list can contain wildcards. * @param directory The provided directory. * @param patterns The list of file name patterns. * @param recursive A flag indicating whether the search is recursive. * @return A list of files. */ fun listFilesByPatterns( directory: File, patterns: List, recursive: Boolean ): List = try { // return the result of the provided // search, with the wildcard filter // and a potential recursive search val pathMatcher = patterns.map { FileSystems.getDefault().getPathMatcher("glob:$it") } if (recursive) directory.walkTopDown().asSequence() .filter { !it.isDirectory } .filter { file -> pathMatcher.any { it.matches(file.toPath().fileName) } }.toList() else directory.listFiles { file: File -> pathMatcher.any { it.matches(file.toPath().fileName) } }!!.toList() } catch (_: Exception) { // if something bad happens, // gracefully fallback to // an empty file list listOf() } /** * Discovers the file through string reference lookup and sets the * configuration accordingly. * * @param reference The string reference. * @throws AraraException Something wrong happened, to be caught in the * higher levels. */ @Throws(AraraException::class) fun discoverFile(reference: String) { lookupFile(reference) ?: throw AraraException( LanguageController.getMessage( Messages.ERROR_DISCOVERFILE_FILE_NOT_FOUND, reference, CommonUtils.fileTypesList ) ) } /** * Performs a file lookup based on a string reference. * * @param reference The file reference as a string. * @return The file as result of the lookup operation. * @throws AraraException Something wrong happened, to be caught in the * higher levels. */ @Throws(AraraException::class) private fun lookupFile(reference: String): File? { val types = Arara.config[AraraSpec.Execution.fileTypes] val file = Arara.config[AraraSpec.Execution.workingDirectory] .resolve(reference).toFile() val name = file.name val parent = FileHandlingUtils.getParentCanonicalFile(file) // direct search, so we are considering // the reference as a complete name val testFile = parent.resolve(name) if (testFile.exists() && testFile.isFile) { types.firstOrNull { testFile.toString().endsWith("." + it.extension) }?.let { Arara.config[AraraSpec.Execution.filePattern] = it.pattern Arara.config[AraraSpec.Execution.reference] = testFile return testFile } } // indirect search; in this case, we are considering // that the file reference has an implicit extension, // so we need to add it and look again // TODO: disable this step in safe mode return types.map { parent.resolve("$name.${it.extension}") to it } .union(types.map { parent.resolve("${name.removeSuffix(".").trim()}.${it.extension}") to it }) .firstOrNull { it.first.exists() && it.first.isFile } ?.let { Arara.config[AraraSpec.Execution.filePattern] = it.second.pattern Arara.config[AraraSpec.Execution.reference] = it.first file } } }