com.redhat.ceylon.common.tools.SourceArgumentsResolver Maven / Gradle / Ivy
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