All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.virtusa.gto.nyql.engine.repo.QScriptsFolder.groovy Maven / Gradle / Ivy

package com.virtusa.gto.nyql.engine.repo

import com.virtusa.gto.nyql.configs.ConfigKeys
import com.virtusa.gto.nyql.exceptions.NyConfigurationException
import com.virtusa.gto.nyql.exceptions.NyException
import com.virtusa.gto.nyql.exceptions.NyScriptNotFoundException
import com.virtusa.gto.nyql.model.QFileSource
import com.virtusa.gto.nyql.model.QScriptMapper
import com.virtusa.gto.nyql.model.QSource
import com.virtusa.gto.nyql.utils.QUtils
import groovy.transform.CompileStatic
import org.slf4j.Logger
import org.slf4j.LoggerFactory

import java.nio.charset.StandardCharsets
import java.nio.file.FileSystems
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.PathMatcher
import java.nio.file.SimpleFileVisitor
import java.nio.file.attribute.BasicFileAttributes
import java.util.stream.Collectors
import java.util.stream.Stream

/**
 * Contains script mapping from a single folder in the system.
 *
 * @author IWEERARATHNA
 */
@CompileStatic
class QScriptsFolder implements QScriptMapper {

    private static final String KEY_INCLUSIONS = 'inclusions'
    private static final String KEY_EXCLUSIONS = 'exclusions'
    private static final String GLOB_NAME = 'glob:'
    private static final Logger LOGGER = LoggerFactory.getLogger(QScriptsFolder)

    final File baseDir
    String inclusionPatterns
    String exclusionPatterns
    private int maxLen = 1

    private final Map fileMap = [:]

    QScriptsFolder(File theBaseDir) {
        baseDir = theBaseDir.getCanonicalFile()
    }

    QScriptsFolder scanDir() {
        // scans the given directory
        LOGGER.info("Loading script files from directory '{}'", baseDir.canonicalPath)

        Path canPath = baseDir.toPath()
        ScriptVisitor visitor = new ScriptVisitor(this, canPath, inclusionPatterns, exclusionPatterns)
        visitor.start()
        prettyPrintFiles()
        this
    }

    private QSource processScript(File file) {
        String relPath = captureFileName(baseDir.toPath().relativize(file.toPath()).toString()).replace('\\', '/')

        String content = readAll(file)
        GroovyCodeSource groovyCodeSource = new GroovyCodeSource(content, relPath, GroovyShell.DEFAULT_CODE_BASE)
        groovyCodeSource.setCachable(true)

        def qSrc = new QFileSource(relPath, file, groovyCodeSource)
        fileMap[relPath] = qSrc
        maxLen = Math.max(relPath.length(), maxLen)
        qSrc
    }

    private static String readAll(File file) {
        file.getText(StandardCharsets.UTF_8.name())
    }

    private static String captureFileName(String path) {
        int lp = path.lastIndexOf('.')
        if (lp > 0) {
            return path.substring(0, lp)
        }
        path
    }

    @SuppressWarnings('UnnecessaryGetter')
    static QScriptsFolder createNew(Map args) throws NyException {
        if (args == null || args.size() == 0 || !args.baseDir) {
            throw new NyConfigurationException('To create a new QScriptsFolder requires at least one parameter with specifying base directory!')
        }

        String path = QUtils.readEnv(ConfigKeys.SYS_SCRIPT_DIR, String.valueOf(args.baseDir))
        File dir = new File(path)
        if (!dir.exists()) {
            String configFilePath = args._location
            if (configFilePath != null) {
                File activeDir = new File(configFilePath).getCanonicalFile()
                if (activeDir.exists() && !dir.isAbsolute()) {
                    QScriptsFolder qScriptsFolder = new QScriptsFolder(activeDir.toPath().resolve(path).toFile())
                    qScriptsFolder.inclusionPatterns = args[KEY_INCLUSIONS] ?: ''
                    qScriptsFolder.exclusionPatterns = args[KEY_EXCLUSIONS] ?: ''
                    return qScriptsFolder.scanDir()
                }
            }
            throw new NyConfigurationException('Given script folder does not exist! [' + path + ']')
        }
        QScriptsFolder qScriptsFolder = new QScriptsFolder(dir)
        qScriptsFolder.inclusionPatterns = args[KEY_INCLUSIONS] ?: ''
        qScriptsFolder.exclusionPatterns = args[KEY_EXCLUSIONS] ?: ''
        qScriptsFolder.scanDir()
    }

    @Override
    QSource map(String id) throws NyScriptNotFoundException {
        QSource source = fileMap[id]
        if (source == null) {
            File scriptFresh = baseDir.toPath().resolve(id).toFile()
            if (scriptFresh.exists()) {
                LOGGER.debug('Loading a fresh script from ' + id + '...')
                source = processScript(scriptFresh)
            } else {
                throw new NyScriptNotFoundException(id)
            }
        }
        if (!source.isValid()) {
            throw new NyScriptNotFoundException(id)
        }
        source
    }

    @Override
    Collection allSources() {
        fileMap.values()
    }

    @Override
    boolean canCacheAtStartup() {
        true
    }

    private void prettyPrintFiles() {
        fileMap.keySet().sort().each {
            String kb = toKB((fileMap[it] as QFileSource).file.length())
            LOGGER.info(' > ' + it.padRight(maxLen + 5) + "${kb.padLeft(6)}")
        }
        LOGGER.info("Found ${fileMap.size()} script(s).")
    }

    private static String toKB(long length) {
        ((int)Math.ceil(length / 1024.0)) + ' KB'
    }

    @CompileStatic
    private static class ScriptVisitor extends SimpleFileVisitor {

        private static final String SPLIT_PATTERN = '[,]'
        private final List inclusions = new LinkedList<>()
        private final List exclusions = new LinkedList<>()
        private final QScriptsFolder scriptsFolder
        private final Path startDir

        @SuppressWarnings("UnnecessaryGetter")
        ScriptVisitor(QScriptsFolder qScriptsFolder, Path rootDir, String patternInclusions, String patternExclusions) {
            scriptsFolder = qScriptsFolder
            startDir = rootDir
            if (patternInclusions.trim().length() > 0) {
                inclusions.addAll(Stream.of(patternInclusions.split(SPLIT_PATTERN))
                        .map { FileSystems.getDefault().getPathMatcher(GLOB_NAME + it) }
                        .collect(Collectors.toList()))
            }
            if (patternExclusions.trim().length() > 0) {
                exclusions.addAll(Stream.of(patternExclusions.split(SPLIT_PATTERN))
                        .map { FileSystems.getDefault().getPathMatcher(GLOB_NAME + it) }
                        .collect(Collectors.toList()))
            }
        }

        private static boolean isEndsWithAny(String text, String... checks) {
            for (String s : checks) {
                if (text.endsWith(s)) {
                    return true
                }
            }
            false
        }

        @Override
        FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            if (!attrs.directory && !isEndsWithAny(file.getFileName().toString().toLowerCase(), '.groovy')) {
                return FileVisitResult.SKIP_SUBTREE
            }

            if (check(file)) {
                if (!attrs.directory) {
                    scriptsFolder.processScript(file.toFile())
                }
                return FileVisitResult.CONTINUE
            }
            FileVisitResult.SKIP_SUBTREE
        }

        private boolean check(Path file) {
            def relPath = startDir.relativize(file)
            if (inclusions.any { it.matches(relPath) }) {
                return true
            }
            if (exclusions.any { it.matches(relPath) }) {
                return false
            }

            return inclusions.size() <= 0
        }

        void start() {
            Files.walkFileTree(startDir, this)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy