/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.compiler.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.compiler.ex.CompileContextEx;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.roots.ModuleExtensionWithSdkOrderEntry;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.OrderEnumerator;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Chunk;
import com.intellij.util.PathsList;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.OrderedSet;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: Sep 29, 2004
*/
public class ModuleChunk extends Chunk<Module> {
private final CompileContextEx myContext;
private final Map<Module, List<VirtualFile>> myModuleToFilesMap = new THashMap<Module, List<VirtualFile>>();
private final Map<VirtualFile, VirtualFile> myTransformedToOriginalMap = new THashMap<VirtualFile, VirtualFile>();
private int mySourcesFilter = ALL_SOURCES;
public ModuleChunk(CompileContextEx context, Chunk<Module> chunk, Map<Module, List<VirtualFile>> moduleToFilesMap) {
super(chunk.getNodes());
myContext = context;
for (final Module module : chunk.getNodes()) {
final List<VirtualFile> files = moduleToFilesMap.get(module);
// Important!!! Collections in the myModuleToFilesMap must be modifiable copies of the corresponding collections
// from the moduleToFilesMap. This is needed to support SourceTransforming compilers
myModuleToFilesMap.put(module, files == null ? Collections.<VirtualFile>emptyList() : new ArrayList<VirtualFile>(files));
}
}
public static final int SOURCES = 0x1;
public static final int TEST_SOURCES = 0x2;
public static final int ALL_SOURCES = SOURCES | TEST_SOURCES;
public void setSourcesFilter(int filter) {
mySourcesFilter = filter;
}
public int getSourcesFilter() {
return mySourcesFilter;
}
public void substituteWithTransformedVersion(Module module, int fileIndex, VirtualFile transformedFile) {
final List<VirtualFile> moduleFiles = getFilesToCompile(module);
final VirtualFile currentFile = moduleFiles.get(fileIndex);
moduleFiles.set(fileIndex, transformedFile);
VirtualFile originalFile = myTransformedToOriginalMap.remove(currentFile);
if (originalFile == null) {
originalFile = currentFile;
}
myTransformedToOriginalMap.put(transformedFile, originalFile);
}
public VirtualFile getOriginalFile(VirtualFile file) {
final VirtualFile original = myTransformedToOriginalMap.get(file);
return original != null? original : file;
}
@NotNull
public List<VirtualFile> getFilesToCompile(Module forModule) {
return myModuleToFilesMap.get(forModule);
}
@NotNull
public List<VirtualFile> getFilesToCompile() {
if (getModuleCount() == 0) {
return Collections.emptyList();
}
final Set<Module> modules = getNodes();
final List<VirtualFile> filesToCompile = new ArrayList<VirtualFile>();
for (final Module module : modules) {
final List<VirtualFile> moduleCompilableFiles = getFilesToCompile(module);
if (mySourcesFilter == ALL_SOURCES) {
filesToCompile.addAll(moduleCompilableFiles);
}
else {
for (final VirtualFile file : moduleCompilableFiles) {
VirtualFile originalFile = myTransformedToOriginalMap.get(file);
if (originalFile == null) {
originalFile = file;
}
if (mySourcesFilter == TEST_SOURCES) {
if (myContext.isInTestSourceContent(originalFile)) {
filesToCompile.add(file);
}
}
else {
if (!myContext.isInTestSourceContent(originalFile)) {
filesToCompile.add(file);
}
}
}
}
}
return filesToCompile;
}
public VirtualFile[] getSourceRoots() {
return getSourceRoots(mySourcesFilter);
}
private VirtualFile[] getSourceRoots(final int sourcesFilter) {
return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile[]>() {
public VirtualFile[] compute() {
return filterRoots(getAllSourceRoots(), getNodes().iterator().next().getProject(), sourcesFilter);
}
});
}
public VirtualFile[] getSourceRoots(final Module module) {
if (!getNodes().contains(module)) {
return VirtualFile.EMPTY_ARRAY;
}
return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile[]>() {
public VirtualFile[] compute() {
return filterRoots(myContext.getSourceRoots(module), module.getProject(), mySourcesFilter);
}
});
}
private VirtualFile[] filterRoots(VirtualFile[] roots, Project project, final int sourcesFilter) {
final List<VirtualFile> filteredRoots = new ArrayList<VirtualFile>(roots.length);
for (final VirtualFile root : roots) {
if (sourcesFilter != ALL_SOURCES) {
if (myContext.isInTestSourceContent(root)) {
if ((sourcesFilter & TEST_SOURCES) == 0) {
continue;
}
}
else {
if ((sourcesFilter & SOURCES) == 0) {
continue;
}
}
}
if (CompilerManager.getInstance(project).isExcludedFromCompilation(root)) {
continue;
}
filteredRoots.add(root);
}
return VfsUtil.toVirtualFileArray(filteredRoots);
}
private VirtualFile[] getAllSourceRoots() {
final Set<Module> modules = getNodes();
Set<VirtualFile> roots = new HashSet<VirtualFile>();
for (final Module module : modules) {
ContainerUtil.addAll(roots, myContext.getSourceRoots(module));
}
return VfsUtil.toVirtualFileArray(roots);
}
public String getCompilationClasspath(SdkType sdkType) {
final OrderedSet<VirtualFile> cpFiles = getCompilationClasspathFiles(sdkType);
return convertToStringPath(cpFiles);
}
public OrderedSet<VirtualFile> getCompilationClasspathFiles(SdkType sdkType) {
return getCompilationClasspathFiles(sdkType, true);
}
public OrderedSet<VirtualFile> getCompilationClasspathFiles(SdkType sdkType, final boolean exportedOnly) {
final Set<Module> modules = getNodes();
OrderedSet<VirtualFile> cpFiles = new OrderedSet<VirtualFile>();
for (final Module module : modules) {
Collections.addAll(cpFiles, orderEnumerator(module, exportedOnly, new AfterSdkOrderEntryCondition(sdkType)).getClassesRoots());
}
return cpFiles;
}
private OrderEnumerator orderEnumerator(Module module, boolean exportedOnly, Condition<OrderEntry> condition) {
OrderEnumerator enumerator = OrderEnumerator.orderEntries(module).compileOnly().satisfying(condition);
if ((mySourcesFilter & TEST_SOURCES) == 0) {
enumerator = enumerator.productionOnly();
}
enumerator = enumerator.recursively();
return exportedOnly ? enumerator.exportedOnly() : enumerator;
}
public String getCompilationBootClasspath(SdkType sdkType) {
return convertToStringPath(getCompilationBootClasspathFiles(sdkType));
}
public OrderedSet<VirtualFile> getCompilationBootClasspathFiles(SdkType sdkType) {
return getCompilationBootClasspathFiles(sdkType, true);
}
public OrderedSet<VirtualFile> getCompilationBootClasspathFiles(SdkType sdkType, final boolean exportedOnly) {
final Set<Module> modules = getNodes();
final OrderedSet<VirtualFile> cpFiles = new OrderedSet<VirtualFile>();
final OrderedSet<VirtualFile> jdkFiles = new OrderedSet<VirtualFile>();
for (final Module module : modules) {
Collections.addAll(cpFiles, orderEnumerator(module, exportedOnly, new BeforeSdkOrderEntryCondition(sdkType, module)).getClassesRoots());
Collections.addAll(jdkFiles, OrderEnumerator.orderEntries(module).sdkOnly().getClassesRoots());
}
cpFiles.addAll(jdkFiles);
return cpFiles;
}
private static String convertToStringPath(final OrderedSet<VirtualFile> cpFiles) {
PathsList classpath = new PathsList();
classpath.addVirtualFiles(cpFiles);
return classpath.getPathsString();
}
//private String tryZipFor(String outputDir) {
// final File zip = CompilerPathsEx.getZippedOutputPath(myContext.getProject(), outputDir);
// if (zip.exists()) {
// try {
// myContext.commitZip(outputDir); // flush unsaved data if any
// }
// catch (IOException e) {
// LOG.info(e);
// }
// return zip.getPath();
// }
// return outputDir;
//}
public int getModuleCount() {
return getNodes().size();
}
public Module[] getModules() {
final Set<Module> nodes = getNodes();
return nodes.toArray(new Module[nodes.size()]);
}
public Module getModule() {
return getNodes().iterator().next();
}
public String getSourcePath() {
return getSourcePath(mySourcesFilter);
}
public String getSourcePath(final int sourcesFilter) {
if (getModuleCount() == 0) {
return "";
}
final VirtualFile[] filteredRoots = getSourceRoots(sourcesFilter);
final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
try {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
for (VirtualFile root : filteredRoots) {
if (buffer.length() > 0) {
buffer.append(File.pathSeparatorChar);
}
buffer.append(root.getPath().replace('/', File.separatorChar));
}
}
});
return buffer.toString();
}
finally {
StringBuilderSpinAllocator.dispose(buffer);
}
}
public Project getProject() {
return myContext.getProject();
}
private static class BeforeSdkOrderEntryCondition implements Condition<OrderEntry> {
private boolean mySdkFound;
private final SdkType mySdkType;
private final Module myOwnerModule;
private BeforeSdkOrderEntryCondition(SdkType sdkType, Module ownerModule) {
mySdkType = sdkType;
myOwnerModule = ownerModule;
}
@Override
public boolean value(OrderEntry orderEntry) {
if (orderEntry instanceof ModuleExtensionWithSdkOrderEntry && myOwnerModule.equals(orderEntry.getOwnerModule())) {
final Sdk sdk = ((ModuleExtensionWithSdkOrderEntry)orderEntry).getSdk();
if(sdk == null || sdk.getSdkType() != mySdkType) {
return true;
}
mySdkFound = true;
}
return !mySdkFound;
}
}
private static class AfterSdkOrderEntryCondition implements Condition<OrderEntry> {
private final SdkType mySdkType;
private boolean mySdkFound;
public AfterSdkOrderEntryCondition(SdkType sdkType) {
mySdkType = sdkType;
}
@Override
public boolean value(OrderEntry orderEntry) {
if (orderEntry instanceof ModuleExtensionWithSdkOrderEntry) {
final Sdk sdk = ((ModuleExtensionWithSdkOrderEntry)orderEntry).getSdk();
if(sdk == null || sdk.getSdkType() != mySdkType) {
return true;
}
mySdkFound = true;
return false;
}
return mySdkFound;
}
}
}