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

com.redhat.ceylon.common.tools.SourceArgumentsResolver Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.common.tools;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.common.ModuleUtil;
import com.redhat.ceylon.common.tool.ToolUsageError;

public class SourceArgumentsResolver {
    private final Iterable sourceDirs;
    private final Iterable resourceDirs;
    private final String[] sourceSuffixes;

    private File cwd;
    private boolean expandSingleSources;
    
    private List srcModules;
    private List resModules;
    private List srcFiles;
    private List resFiles;
    
    public SourceArgumentsResolver(Iterable sourceDirs, Iterable resourceDirs, String... sourceSuffixes) {
        this.sourceDirs = sourceDirs;
        this.resourceDirs = resourceDirs;
        this.sourceSuffixes = sourceSuffixes;
        this.srcModules = new LinkedList();
        this.resModules = new LinkedList();
        this.srcFiles = new LinkedList();
        this.resFiles = new LinkedList();
    }
    
    public SourceArgumentsResolver cwd(File cwd) {
        this.cwd = cwd;
        return this;
    }

    /**
     * Setting this to true will cause single source files to be expanded to include
     * all other files belonging to their module. This is specifically done for
     * compiler backends that are unable to perform single-file compilations.
     * @param expandSingleSources
     * @return This object for chaining
     */
    public SourceArgumentsResolver expandSingleSources(boolean expandSingleSources) {
        this.expandSingleSources = expandSingleSources;
        return this;
    }

    public List getSourceModules() {
        return srcModules;
    }

    public List getResourceModules() {
        return resModules;
    }

    public List getSourceFiles() {
        return srcFiles;
    }

    public List getResourceFiles() {
        return resFiles;
    }

    public void expandAndParse(List modulesOrFiles, Backend forBackend) throws IOException {
        Iterable srcs = FileUtil.applyCwd(cwd, sourceDirs);
        List expandedModulesOrFiles = ModuleWildcardsHelper.expandWildcards(srcs, modulesOrFiles, forBackend);
        parse(expandedModulesOrFiles);
    }
    
    public void parse(List modulesOrFiles) throws IOException {
        HashSet srcMods = new HashSet();
        HashSet resMods = new HashSet();
        HashSet singleFileMods = new HashSet();
        srcFiles = new LinkedList();
        resFiles = new LinkedList();
        Iterable srcs = FileUtil.applyCwd(cwd, sourceDirs);
        Iterable resrcs = FileUtil.applyCwd(cwd, resourceDirs);
        for (String moduleOrFile : modulesOrFiles) {
            File file = new File(moduleOrFile);
            if (file.isFile()) {
                // It's a single (re)source file instead of a module name, so let's check
                // if it's really located in one of the defined (re)source folders
                File path;
                if (hasAcceptedSuffix(file, sourceSuffixes)) {
                    path = FileUtil.selectPath(srcs, moduleOrFile);
                    if (path == null) {
                        String srcPath = sourceDirs.toString();
                        throw new ToolUsageError(CeylonToolMessages.msg("error.not.in.source.path", moduleOrFile, srcPath));
                    }
                    if (!expandSingleSources) {
                        String relFileName = FileUtil.relativeFile(srcs, file.getPath());
                        srcFiles.add(new File(path, relFileName));
                    } else {
                        // Instead of adding the source file itself we remember
                        // its module name and at the end we expand that and
                        // add all its files
                        singleFileMods.add(moduleName(srcs, path, file));
                    }
                    // Determine the module path from the file path
                    srcMods.add(moduleName(srcs, path, file));
                } else {
                    if (resrcs != null) {
                        path = FileUtil.selectPath(resrcs, moduleOrFile);
                        if (path == null) {
                            String resrcPath = resourceDirs.toString();
                            throw new ToolUsageError(CeylonToolMessages.msg("error.not.in.resource.path", moduleOrFile, resrcPath));
                        }
                        String relFileName = FileUtil.relativeFile(resrcs, file.getPath());
                        resFiles.add(new File(path, relFileName));
                        // Determine the module path from the file path
                        resMods.add(moduleName(srcs, path, file));
                    }
                }
            } else {
                visitModuleFiles(srcFiles, srcs, moduleOrFile, sourceSuffixes);
                srcMods.add(moduleOrFile);
                if (resrcs != null) {
                    visitModuleFiles(resFiles, resrcs, moduleOrFile, null);
                    resMods.add(moduleOrFile);
                }
            }
            if (srcFiles.isEmpty() && resFiles.isEmpty()) {
                throw new ToolUsageError(CeylonToolMessages.msg("error.no.files", moduleOrFile));
            }
        }
        // Now expand the sources of any single source modules we encountered
        for (String modName : singleFileMods) {
            visitModuleFiles(srcFiles, srcs, modName, sourceSuffixes);
        }
        srcModules = new ArrayList(srcMods);
        resModules = new ArrayList(resMods);
    }

    private void visitModuleFiles(List files, Iterable paths, String modName, String[] suffixes) throws IOException {
        if (ModuleUtil.isDefaultModule(modName)) {
            visitFiles(paths, null, new RootedFileVisitor(files, true, suffixes));
        } else {
            File modPath = ModuleUtil.moduleToPath(modName);
            if (isModuleFolder(modPath)) {
                visitFiles(paths, modPath, new RootedFileVisitor(files, false, suffixes));
            } else {
                File dir = searchModulePath(modPath);
                if (dir == null || !dir.isDirectory()) {
                    if (modName.contains("/")) {
                        throw new ToolUsageError(CeylonToolMessages.msg("error.invalid.module.or.file", modName));
                    }
                    for (String suffix : suffixes) {
                        if (modName.endsWith(suffix)) {
                            throw new ToolUsageError(CeylonToolMessages.msg("error.file.not.found", modName));
                        }
                    }
                    String ps = sourceDirs.toString();
                    ps = ps.substring(1, ps.length() - 1);
                    throw new ToolUsageError(CeylonToolMessages.msg("error.module.not.found", modName, ps));
                } else {
                    throw new ToolUsageError(CeylonToolMessages.msg("error.not.module", modName));
                }
            }
        }
    }
    
    private static void visitFiles(Iterable dirs, File modPath, RootedFileVisitor visitor) throws IOException {
        for (File dir : dirs) {
            visitFiles(dir, modPath, visitor);
        }
    }
    
    private static void visitFiles(File dir, File modPath, RootedFileVisitor visitor) throws IOException {
        File moduleDir = dir;
        if (modPath != null) {
            moduleDir = new File(dir, modPath.getPath());
        }
        visitor.rootPath = dir;
        if (moduleDir.isDirectory()) {
            Files.walkFileTree(moduleDir.toPath(), visitor);
        }
    }
    
    private class RootedFileVisitor extends SimpleFileVisitor {
        private final List result;
        private final boolean excludeModules;
        private final String[] acceptedSuffixes;
        
        public File rootPath;
        
        public RootedFileVisitor(List result, boolean excludeModules, String[] acceptedFiles) {
            this.result = result;
            this.excludeModules = excludeModules;
            this.acceptedSuffixes = acceptedFiles;
        }
        
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            if (excludeModules && isModuleFolder(rootPath, dir.toFile())) {
                return FileVisitResult.SKIP_SUBTREE;
            }
            return super.preVisitDirectory(dir, attrs);
        }
        
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            boolean add = true;
            if (acceptedSuffixes != null) {
                add = hasAcceptedSuffix(file.toFile(), acceptedSuffixes);
            }
            if (add) {
                result.add(file.toFile());
            }
            return super.visitFile(file, attrs);
        }
    }
    
    private static boolean hasAcceptedSuffix(File file, String... suffixes) {
        for (String ext : suffixes) {
            if (file.toString().endsWith(ext)) {
                return true;
            }
        }
        return false;
    }

    private boolean isModuleFolder(File rootPath, File modPath) {
        File relPath = FileUtil.relativeFile(rootPath, modPath);
        return isModuleFolder(relPath);
    }

    private boolean isModuleFolder(File modPath) {
        Iterable srcs = FileUtil.applyCwd(cwd, sourceDirs);
        return ModuleUtil.isModuleFolder(srcs, modPath);
    }

    private File searchModulePath(File modPath) {
        Iterable srcs = FileUtil.applyCwd(cwd, sourceDirs);
        return FileUtil.searchPaths(srcs, modPath.getPath());
    }
    
    private String moduleName(Iterable srcs, File rootPath, File file) {
        File relFile = FileUtil.relativeFile(rootPath, file);
        return ModuleUtil.moduleName(srcs, relFile);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy