package org.jetbrains.jps;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: 9/30/11
*/
public class ProjectPaths {
public static final Key<ProjectPaths> KEY = Key.create("_project_paths_");
@NotNull
private final Project myProject;
@Nullable
private final File myProjectTargetDir;
private final Map<Module, File> myCustomModuleOutputDir = new HashMap<Module, File>();
private final Map<Module, File> myCustomModuleTestOutputDir = new HashMap<Module, File>();
//private final Map<ClasspathKind, Map<ModuleChunk, List<String>>> myCachedClasspath = new HashMap<ClasspathKind, Map<ModuleChunk, List<String>>>();
public ProjectPaths(Project project) {
this(project, null);
}
public ProjectPaths(Project project, @Nullable File projectTargetDir) {
myProject = project;
myProjectTargetDir = projectTargetDir;
}
public Collection<File> getClasspathFiles(Module module, ClasspathKind kind) {
final Set<File> files = new LinkedHashSet<File>();
collectClasspath(module, kind, files, new HashSet<Module>(), false, !kind.isRuntime(), false, ACCEPT_ALL);
return files;
}
public Collection<File> getClasspathFiles(ModuleChunk chunk, ClasspathKind kind) {
return getClasspathFiles(chunk, kind, !kind.isRuntime());
}
public List<String> getClasspath(ModuleChunk chunk, ClasspathKind kind) {
return getPathsList(getClasspathFiles(chunk, kind));
}
public Collection<File> getClasspathFiles(ModuleChunk chunk, ClasspathKind kind, final boolean excludeMainModuleOutput) {
return getClasspathFiles(chunk, kind, excludeMainModuleOutput, ClasspathPart.WHOLE);
}
public Collection<File> getPlatformCompilationClasspath(ModuleChunk chunk, boolean includeTests, boolean excludeMainModuleOutput) {
return getClasspathFiles(chunk, ClasspathKind.compile(includeTests), excludeMainModuleOutput, ClasspathPart.BEFORE_JDK);
}
public Collection<File> getCompilationClasspath(ModuleChunk chunk, boolean includeTests, boolean excludeMainModuleOutput) {
return getClasspathFiles(chunk, ClasspathKind.compile(includeTests), excludeMainModuleOutput, ClasspathPart.AFTER_JDK);
}
private Collection<File> getClasspathFiles(ModuleChunk chunk, ClasspathKind kind, final boolean excludeMainModuleOutput, ClasspathPart classpathPart) {
final Set<File> files = new LinkedHashSet<File>();
for (Module module : chunk.getModules()) {
final ClasspathItemFilter filter = classpathPart == ClasspathPart.WHOLE ? ACCEPT_ALL :
classpathPart == ClasspathPart.BEFORE_JDK ? new BeforeSdkItemFilter(module) : new NotFilter(new BeforeSdkItemFilter(module));
collectClasspath(module, kind, files, new HashSet<Module>(), false, excludeMainModuleOutput, false, filter);
}
return files;
}
private void collectClasspath(Module module, ClasspathKind kind, Set<File> classpath, Set<Module> processed, boolean exportedOnly,
boolean excludeMainModuleOutput, final boolean excludeSdk, ClasspathItemFilter filter) {
if (!processed.add(module)) {
return;
}
for (ClasspathItem it : module.getClasspath(kind, exportedOnly)) {
if (!filter.accept(module, it) || it instanceof Sdk && excludeSdk) {
continue;
}
if (it instanceof ModuleSourceEntry) {
final Module dep = ((ModuleSourceEntry) it).getModule();
if (!excludeMainModuleOutput && kind.isTestsIncluded()) {
classpath.add(getModuleOutputDir(dep, true));
}
if (!excludeMainModuleOutput || kind.isTestsIncluded()) {
classpath.add(getModuleOutputDir(dep, false));
}
}
else if (it instanceof Module) {
collectClasspath((Module) it, kind, classpath, processed, !kind.isRuntime(), false, true, filter);
}
else {
addFiles(classpath, it.getClasspathRoots(kind));
}
}
}
private void addFiles(Set<File> files, final Collection<String> paths) {
for (String root : paths) {
files.add(new File(root));
}
}
public List<String> getSourcePathsForModuleWithDependents(ModuleChunk chunk, boolean includeTests) {
Set<File> sourcePaths = new LinkedHashSet<File>();
final HashSet<Module> processed = new HashSet<Module>();
for (Module module : chunk.getModules()) {
collectSourcePaths(module, ClasspathKind.compile(includeTests), sourcePaths, processed);
}
return getPathsList(sourcePaths);
}
public static List<String> getPathsList(Collection<File> files) {
final List<String> result = new ArrayList<String>();
for (File file : files) {
result.add(file.getPath());
}
return result;
}
private void collectSourcePaths(Module module, ClasspathKind kind, Set<File> sourcePaths, Set<Module> processed) {
if (!processed.add(module)) return;
for (ClasspathItem item : module.getClasspath(kind, false)) {
if (item instanceof ModuleSourceEntry) {
final Module dep = ((ModuleSourceEntry) item).getModule();
addFiles(sourcePaths, dep.getSourceRoots());
if (kind.isTestsIncluded()) {
addFiles(sourcePaths, dep.getTestRoots());
}
}
else if (item instanceof Module) {
collectSourcePaths(module, kind, sourcePaths, processed);
}
}
}
public void setCustomModuleOutputDir(Module module, boolean forTests, File outputDir) {
(forTests ? myCustomModuleTestOutputDir : myCustomModuleOutputDir).put(module, outputDir);
}
public File getModuleOutputDir(Module module, boolean forTests) {
File customOutput = (forTests ? myCustomModuleTestOutputDir : myCustomModuleOutputDir).get(module);
if (customOutput != null) {
return customOutput;
}
if (myProjectTargetDir != null) {
final File basePath = new File(myProjectTargetDir, forTests ? "test" : "production");
return new File(basePath, module.getName());
}
final String path = forTests ? module.getTestOutputPath() : module.getOutputPath();
return path != null ? new File(path) : null;
}
public List<String> getProjectRuntimeClasspath(boolean includeTests) {
Set<File> classpath = new LinkedHashSet<File>();
final ClasspathKind kind = ClasspathKind.runtime(includeTests);
for (Module module : myProject.getModules().values()) {
collectClasspath(module, kind, classpath, new HashSet<Module>(), false, false, false, WITHOUT_DEP_MODULES);
}
return getPathsList(classpath);
}
private static interface ClasspathItemFilter {
boolean accept(Module module, ClasspathItem item);
}
private static enum ClasspathPart {WHOLE, BEFORE_JDK, AFTER_JDK}
private static final ClasspathItemFilter ACCEPT_ALL = new ClasspathItemFilter() {
public boolean accept(Module module, ClasspathItem item) {
return true;
}
};
private static final ClasspathItemFilter WITHOUT_DEP_MODULES = new ClasspathItemFilter() {
public boolean accept(Module module, ClasspathItem item) {
return !(item instanceof Module);
}
};
private static class BeforeSdkItemFilter implements ClasspathItemFilter {
private Module myModule;
private boolean mySdkFound;
private BeforeSdkItemFilter(Module module) {
myModule = module;
}
public boolean accept(Module module, ClasspathItem item) {
if (myModule.equals(module) && item instanceof Sdk) {
mySdkFound = true;
return true;
}
return !mySdkFound && !(item instanceof Sdk);
}
}
private static class NotFilter implements ClasspathItemFilter {
private ClasspathItemFilter myFilter;
private NotFilter(ClasspathItemFilter filter) {
myFilter = filter;
}
public boolean accept(Module module, ClasspathItem item) {
return !myFilter.accept(module, item);
}
}
}