annotation processors:

1. invoke per-module
2. reworked handling of output paths for generated sources (UI coming soon)
This commit is contained in:
Eugene Zhuravlev
2009-11-16 12:28:30 +03:00
parent 06c3d2ef3d
commit 5d3a8e4205
17 changed files with 1138 additions and 116 deletions
@@ -80,6 +80,8 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
private boolean myEnableAnnotationProcessors = false;
private final Map<String, String> myProcessorsMap = new HashMap<String, String>(); // map: AnnotationProcessorName -> options
private boolean myObtainProcessorsFromClasspath = true;
private boolean myStoreGenerateSourcesUnderModuleContent = false;
private String myGeneratedDirName = "generated";
private String myProcessorPath = "";
private final Set<Module> myExcludedModules = new HashSet<Module>();
private final Set<String> myExcludedModuleNames = new HashSet<String>();
@@ -310,6 +312,24 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
myObtainProcessorsFromClasspath = obtainProcessorsFromClasspath;
}
public boolean isStoreGenerateSourcesUnderModuleContent(Module module) {
// todo: make this per-module setting
return myStoreGenerateSourcesUnderModuleContent;
}
public void setStoreGenerateSourcesUnderModuleContent(boolean storeGenerateSourcesUnderModuleContent) {
myStoreGenerateSourcesUnderModuleContent = storeGenerateSourcesUnderModuleContent;
}
@NotNull
public String getGeneratedDirName() {
return myGeneratedDirName;
}
public void setGeneratedDirName(String generatedDirName) {
myGeneratedDirName = generatedDirName;
}
public String getProcessorPath() {
return myProcessorPath;
}
@@ -522,6 +542,8 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
if (annotationProcessingSettings != null) {
myEnableAnnotationProcessors = Boolean.valueOf(annotationProcessingSettings.getAttributeValue("enabled", "false"));
myObtainProcessorsFromClasspath = Boolean.valueOf(annotationProcessingSettings.getAttributeValue("useClasspath", "true"));
myStoreGenerateSourcesUnderModuleContent = Boolean.valueOf(annotationProcessingSettings.getAttributeValue("storeGeneratedUnderContent", "false"));
myGeneratedDirName = annotationProcessingSettings.getAttributeValue("generatedDirName", "generated");
final StringBuilder pathBuilder = new StringBuilder();
for (Element pathElement : ((Collection<Element>)annotationProcessingSettings.getChildren("processorPath"))) {
@@ -596,6 +618,8 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements
parentNode.addContent(annotationProcessingSettings);
annotationProcessingSettings.setAttribute("enabled", String.valueOf(myEnableAnnotationProcessors));
annotationProcessingSettings.setAttribute("useClasspath", String.valueOf(myObtainProcessorsFromClasspath));
annotationProcessingSettings.setAttribute("storeGeneratedUnderContent", String.valueOf(myStoreGenerateSourcesUnderModuleContent));
annotationProcessingSettings.setAttribute("generatedDirName", myGeneratedDirName);
if (myProcessorPath.length() > 0) {
final StringTokenizer tokenizer = new StringTokenizer(myProcessorPath, File.pathSeparator, false);
while (tokenizer.hasMoreTokens()) {
@@ -64,8 +64,8 @@ public class CompilerManagerImpl extends CompilerManager {
myEventPublisher = messageBus.syncPublisher(CompilerTopics.COMPILATION_STATUS);
// predefined compilers
addTranslatingCompiler(new JavaCompiler(myProject), new HashSet<FileType>(Arrays.asList(StdFileTypes.JAVA)), new HashSet<FileType>(Arrays.asList(StdFileTypes.CLASS)));
addCompiler(new AnnotationProcessingCompiler(project));
addTranslatingCompiler(new AnnotationProcessingCompiler(project), new HashSet<FileType>(Arrays.asList(StdFileTypes.JAVA)), new HashSet<FileType>(Arrays.asList(StdFileTypes.JAVA, StdFileTypes.CLASS)));
addTranslatingCompiler(new JavaCompiler(project), new HashSet<FileType>(Arrays.asList(StdFileTypes.JAVA)), new HashSet<FileType>(Arrays.asList(StdFileTypes.CLASS)));
addCompiler(new ResourceCompiler(project, compilerConfiguration));
addCompiler(new RmicCompiler());
addCompiler(new IncrementalArtifactsCompiler());
@@ -51,6 +51,7 @@ import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.*;
@@ -138,6 +139,13 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
return true;
}
}
final Module module = getModuleByFile(file);
if (module != null) {
final String procGenRoot = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
if (VfsUtil.isAncestor(new File(procGenRoot), new File(file.getPath()), true)) {
return true;
}
}
return false;
}
@@ -53,10 +53,7 @@ import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.openapi.roots.ui.configuration.CommonContentEntriesEditor;
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
@@ -145,16 +142,24 @@ public class CompileDriver {
myGenerationCompilerModuleToOutputDirMap = new HashMap<Pair<IntermediateOutputCompiler, Module>, Pair<VirtualFile, VirtualFile>>();
final LocalFileSystem lfs = LocalFileSystem.getInstance();
final IntermediateOutputCompiler[] generatingCompilers = CompilerManager.getInstance(myProject).getCompilers(IntermediateOutputCompiler.class, myCompilerFilter);
if (generatingCompilers.length > 0) {
final Module[] allModules = ModuleManager.getInstance(myProject).getModules();
final Module[] allModules = ModuleManager.getInstance(myProject).getModules();
final CompilerConfiguration config = CompilerConfiguration.getInstance(project);
for (Module module : allModules) {
for (IntermediateOutputCompiler compiler : generatingCompilers) {
for (final Module module : allModules) {
final VirtualFile productionOutput = lookupVFile(compiler, module, false);
final VirtualFile testOutput = lookupVFile(compiler, module, true);
final Pair<IntermediateOutputCompiler, Module> pair = new Pair<IntermediateOutputCompiler, Module>(compiler, module);
final Pair<VirtualFile, VirtualFile> outputs = new Pair<VirtualFile, VirtualFile>(productionOutput, testOutput);
myGenerationCompilerModuleToOutputDirMap.put(pair, outputs);
final VirtualFile productionOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, false));
final VirtualFile testOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, true));
final Pair<IntermediateOutputCompiler, Module> pair = new Pair<IntermediateOutputCompiler, Module>(compiler, module);
final Pair<VirtualFile, VirtualFile> outputs = new Pair<VirtualFile, VirtualFile>(productionOutput, testOutput);
myGenerationCompilerModuleToOutputDirMap.put(pair, outputs);
}
if (config.isAnnotationProcessorsEnabled()) {
if (!config.getExcludedModules().contains(module)) {
final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
if (path != null) {
lookupVFile(lfs, path); // ensure the file is created and added to VFS
}
}
}
}
@@ -334,6 +339,27 @@ public class CompileDriver {
return scope;
}
private void attachAnnotationProcessorsOutputDirectories(CompileContextEx context) {
final LocalFileSystem lfs = LocalFileSystem.getInstance();
final Set<Module> affected = new HashSet<Module>(Arrays.asList(context.getCompileScope().getAffectedModules()));
for (Module module : affected) {
final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
if (path == null) {
continue;
}
final VirtualFile vFile = lfs.findFileByPath(path);
if (vFile == null) {
continue;
}
if (ModuleRootManager.getInstance(module).getFileIndex().isInSourceContent(vFile)) {
// no need to add, is already marked as source
continue;
}
context.addScope(new FileSetCompileScope(Collections.singletonList(vFile), new Module[]{module}));
context.assignModule(vFile, module, false);
}
}
public static final Key<Long> COMPILATION_START_TIMESTAMP = Key.create("COMPILATION_START_TIMESTAMP");
private void startup(final CompileScope scope,
@@ -365,7 +391,8 @@ public class CompileDriver {
compileContext.assignModule(outputs.getFirst(), module, false);
compileContext.assignModule(outputs.getSecond(), module, true);
}
attachAnnotationProcessorsOutputDirectories(compileContext);
compileTask.start(new Runnable() {
public void run() {
long start = System.currentTimeMillis();
@@ -1147,9 +1174,21 @@ public class CompileDriver {
private Set<File> getAllOutputDirectories() {
final Set<File> outputDirs = new OrderedSet<File>((TObjectHashingStrategy<File>)TObjectHashingStrategy.CANONICAL);
for (final String path : CompilerPathsEx.getOutputPaths(ModuleManager.getInstance(myProject).getModules())) {
final Module[] modules = ModuleManager.getInstance(myProject).getModules();
for (final String path : CompilerPathsEx.getOutputPaths(modules)) {
outputDirs.add(new File(path));
}
final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
if (config.isAnnotationProcessorsEnabled()) {
for (Module module : modules) {
if (!config.getExcludedModules().contains(module)) {
final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
if (path != null) {
outputDirs.add(new File(path));
}
}
}
}
return outputDirs;
}
@@ -1422,6 +1461,7 @@ public class CompileDriver {
context.getProgressIndicator().pushState();
final boolean[] wereFilesDeleted = new boolean[]{false};
boolean traverseRootsProcessed = false;
try {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
@@ -1469,15 +1509,17 @@ public class CompileDriver {
context.requestRebuildNextTime(e.getMessage());
}
}
if ((wereFilesDeleted[0] || !toCompile.isEmpty()) && context.getMessageCount(CompilerMessageCategory.ERROR) == 0) {
final boolean hadUnprocessedTraverseRoots = context.getDependencyCache().hasUnprocessedTraverseRoots();
if ((wereFilesDeleted[0] || hadUnprocessedTraverseRoots || !toCompile.isEmpty()) && context.getMessageCount(CompilerMessageCategory.ERROR) == 0) {
compiler.compile(context, moduleChunk, VfsUtil.toVirtualFileArray(toCompile), sink);
traverseRootsProcessed = hadUnprocessedTraverseRoots != context.getDependencyCache().hasUnprocessedTraverseRoots();
}
}
finally {
context.getProgressIndicator().popState();
}
return !toCompile.isEmpty() || wereFilesDeleted[0];
return !toCompile.isEmpty() || traverseRootsProcessed || wereFilesDeleted[0];
}
private static boolean syncOutputDir(final CompileContextEx context, final Collection<Trinity<File, String, Boolean>> toDelete) throws CacheCorruptedException {
@@ -1787,8 +1829,10 @@ public class CompileDriver {
private boolean validateCompilerConfiguration(final CompileScope scope, boolean checkOutputAndSourceIntersection) {
final Module[] scopeModules = scope.getAffectedModules()/*ModuleManager.getInstance(myProject).getModules()*/;
final List<String> modulesWithoutOutputPathSpecified = new ArrayList<String>();
boolean isProjectCompilePathSpecified = true;
final List<String> modulesWithoutJdkAssigned = new ArrayList<String>();
final Set<File> nonExistingOutputPaths = new HashSet<File>();
final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
for (final Module module : scopeModules) {
final boolean hasSources = hasSources(module, false);
@@ -1830,6 +1874,23 @@ public class CompileDriver {
modulesWithoutOutputPathSpecified.add(module.getName());
}
}
if (config.isAnnotationProcessorsEnabled() && !config.getExcludedModules().contains(module)) {
final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
if (path == null) {
if (CompilerProjectExtension.getInstance(module.getProject()).getCompilerOutputUrl() == null) {
isProjectCompilePathSpecified = false;
}
else {
modulesWithoutOutputPathSpecified.add(module.getName());
}
}
else {
final File file = new File(path);
if (!file.exists()) {
nonExistingOutputPaths.add(file);
}
}
}
}
}
if (!modulesWithoutJdkAssigned.isEmpty()) {
@@ -1837,6 +1898,17 @@ public class CompileDriver {
return false;
}
if (!isProjectCompilePathSpecified) {
final String message = CompilerBundle.message("error.project.output.not.specified");
if (ApplicationManager.getApplication().isUnitTestMode()) {
LOG.error(message);
}
Messages.showMessageDialog(myProject, message, CommonBundle.getErrorTitle(), Messages.getErrorIcon());
ProjectSettingsService.getInstance(myProject).openProjectSettings();
return false;
}
if (!modulesWithoutOutputPathSpecified.isEmpty()) {
showNotSpecifiedError("error.output.not.specified", modulesWithoutOutputPathSpecified, CommonContentEntriesEditor.NAME);
return false;
@@ -1880,7 +1952,6 @@ public class CompileDriver {
}
}
final List<Chunk<Module>> chunks = ModuleCompilerUtil.getSortedModuleChunks(myProject, Arrays.asList(scopeModules));
final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
for (final Chunk<Module> chunk : chunks) {
final Set<Module> chunkModules = chunk.getNodes();
if (chunkModules.size() <= 1) {
@@ -2071,9 +2142,8 @@ public class CompileDriver {
ProjectSettingsService.getInstance(myProject).showModuleConfigurationDialog(moduleNameToSelect, tabNameToSelect, false);
}
private static VirtualFile lookupVFile(final IntermediateOutputCompiler compiler, final Module module, final boolean forTestSources) {
final File file = new File(CompilerPaths.getGenerationOutputPath(compiler, module, forTestSources));
final LocalFileSystem lfs = LocalFileSystem.getInstance();
private static VirtualFile lookupVFile(final LocalFileSystem lfs, final String path) {
final File file = new File(path);
VirtualFile vFile = lfs.findFileByIoFile(file);
if (vFile != null) {
@@ -24,6 +24,7 @@ package com.intellij.compiler.impl.javaCompiler;
import com.intellij.compiler.CompilerConfiguration;
import com.intellij.compiler.CompilerConfigurationImpl;
import com.intellij.compiler.CompilerException;
import com.intellij.compiler.impl.CompileContextExProxy;
import com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler;
import com.intellij.compiler.make.CacheCorruptedException;
import com.intellij.openapi.application.ApplicationManager;
@@ -33,20 +34,24 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Chunk;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.IOException;
import java.util.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class AnnotationProcessingCompiler implements SourceProcessingCompiler{
public class AnnotationProcessingCompiler implements TranslatingCompiler{
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.JavaCompiler");
private final Project myProject;
private CompilerConfiguration myConfig;
public AnnotationProcessingCompiler(Project project) {
myProject = project;
myConfig = CompilerConfiguration.getInstance(project);
}
@NotNull
@@ -54,74 +59,48 @@ public class AnnotationProcessingCompiler implements SourceProcessingCompiler{
return CompilerBundle.message("annotation.processing.compiler.description");
}
@NotNull
public ProcessingItem[] getProcessingItems(CompileContext context) {
final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
if (!config.isAnnotationProcessorsEnabled()) {
return ProcessingItem.EMPTY_ARRAY;
public boolean isCompilableFile(VirtualFile file, CompileContext context) {
if (!myConfig.isAnnotationProcessorsEnabled()) {
return false;
}
return file.getFileType() == StdFileTypes.JAVA && !isExcludedFromAnnotationProcessing(file, context);
}
public void compile(final CompileContext context, final Chunk<Module> moduleChunk, final VirtualFile[] files, OutputSink sink) {
if (!myConfig.isAnnotationProcessorsEnabled()) {
return;
}
final VirtualFile[] files = context.getCompileScope().getFiles(StdFileTypes.JAVA, true);
final List<ProcessingItem> items = new ArrayList<ProcessingItem>(files.length);
final Set<Module> excludedModules = config.getExcludedModules();
for (final VirtualFile file : files) {
if (excludedModules.size() != 0 && excludedModules.contains(context.getModuleByFile(file))) {
continue;
final LocalFileSystem lfs = LocalFileSystem.getInstance();
final CompileContextEx _context = new CompileContextExProxy((CompileContextEx)context) {
public VirtualFile getModuleOutputDirectory(Module module) {
final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
return path != null? lfs.findFileByPath(path) : null;
}
if (config.isExcludedFromCompilation(file)) {
continue;
}
items.add(new MyProcessingItem(file));
}
return items.toArray(new ProcessingItem[items.size()]);
}
public ProcessingItem[] process(CompileContext context, ProcessingItem[] items) {
final VirtualFile[] files = new VirtualFile[items.length];
for (int idx = 0; idx < items.length; idx++) {
files[idx] = items[idx].getFile();
}
compile(context, files);
return context.getMessageCount(CompilerMessageCategory.ERROR) == 0? items : ProcessingItem.EMPTY_ARRAY;
}
public ValidityState createValidityState(DataInput in) throws IOException {
return null;
}
private void compile(final CompileContext context, final VirtualFile[] files) {
};
final JavacCompiler javacCompiler = getBackEndCompiler();
final boolean processorMode = javacCompiler.setAnnotationProcessorMode(true);
final Chunk<Module> dummyChunk = new Chunk<Module>(Collections.<Module>emptySet()); // TODO!
final BackendCompilerWrapper wrapper = new BackendCompilerWrapper(dummyChunk, myProject, Arrays.asList(files), (CompileContextEx)context, javacCompiler, DummySink.INSTANCE);
final BackendCompilerWrapper wrapper = new BackendCompilerWrapper(moduleChunk, myProject, Arrays.asList(files), _context, javacCompiler, sink);
try {
wrapper.compile();
}
catch (CompilerException e) {
context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
_context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
}
catch (CacheCorruptedException e) {
LOG.info(e);
context.requestRebuildNextTime(e.getMessage());
_context.requestRebuildNextTime(e.getMessage());
}
finally {
javacCompiler.setAnnotationProcessorMode(processorMode);
final Set<VirtualFile> dirsToRefresh = new HashSet<VirtualFile>();
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final Set<Module> modules = new HashSet<Module>();
for (VirtualFile file : files) {
final Module module = context.getModuleByFile(file);
if (module != null) {
modules.add(module);
for (Module module : moduleChunk.getNodes()) {
final VirtualFile out = _context.getModuleOutputDirectory(module);
if (out != null) {
dirsToRefresh.add(out);
}
}
for (Module module : modules) {
dirsToRefresh.add(context.getModuleOutputDirectory(module));
dirsToRefresh.add(context.getModuleOutputDirectoryForTests(module));
// todo: Some annotation processors put files into the source code. So need to refresh module source roots.
// It is an open question whether we shall support such processors
//dirsToRefresh.addAll(Arrays.asList(ModuleRootManager.getInstance(module).getSourceRoots()));
}
}
});
for (VirtualFile root : dirsToRefresh) {
@@ -130,6 +109,21 @@ public class AnnotationProcessingCompiler implements SourceProcessingCompiler{
}
}
private boolean isExcludedFromAnnotationProcessing(VirtualFile file, CompileContext context) {
final Module module = context.getModuleByFile(file);
if (module != null) {
if (myConfig.getExcludedModules().contains(module)) {
return true;
}
final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
final VirtualFile generationDir = LocalFileSystem.getInstance().findFileByPath(path);
if (VfsUtil.isAncestor(generationDir, file, false)) {
return true;
}
}
return myConfig.isExcludedFromCompilation(file);
}
public boolean validateConfiguration(CompileScope scope) {
final JavacCompiler compiler = getBackEndCompiler();
final boolean previousValue = compiler.setAnnotationProcessorMode(true);
@@ -146,26 +140,4 @@ public class AnnotationProcessingCompiler implements SourceProcessingCompiler{
return configuration.getJavacCompiler();
}
private static class MyProcessingItem implements ProcessingItem {
private final VirtualFile myFile;
public MyProcessingItem(VirtualFile file) {
myFile = file;
}
@NotNull
public VirtualFile getFile() {
return myFile;
}
public ValidityState getValidityState() {
return null;
}
}
private static class DummySink implements TranslatingCompiler.OutputSink {
public static final DummySink INSTANCE = new DummySink();
public void add(String outputRoot, Collection<TranslatingCompiler.OutputItem> items, VirtualFile[] filesToRecompile) {
}
}
}
@@ -0,0 +1,414 @@
/*
* 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.
*/
/*
* @author: Eugene Zhuravlev
* Date: Jan 24, 2003
* Time: 4:25:47 PM
*/
package com.intellij.compiler.impl.javaCompiler;
import com.intellij.compiler.CompilerException;
import com.intellij.compiler.OutputParser;
import com.intellij.compiler.impl.CompilerUtil;
import com.intellij.compiler.make.CacheCorruptedException;
import com.intellij.ide.util.projectWizard.JavaModuleBuilder;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerBundle;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.compiler.TranslatingCompiler;
import com.intellij.openapi.compiler.ex.CompileContextEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.JavaModuleType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Chunk;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class AnnotationProcessorsRunner {
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.BackendCompilerWrapper");
private final BackendCompiler myCompiler;
private final Set<VirtualFile> mySuccesfullyCompiledJavaFiles; // VirtualFile
private final CompileContextEx myCompileContext;
private final List<VirtualFile> myFilesToCompile;
private final TranslatingCompiler.OutputSink mySink;
private final Chunk<Module> myChunk;
private final Project myProject;
private final Set<VirtualFile> myFilesToRecompile;
private final ProjectFileIndex myProjectFileIndex;
private long myCompilationDuration = 0L;
public AnnotationProcessorsRunner(Chunk<Module> chunk, @NotNull final Project project,
@NotNull List<VirtualFile> filesToCompile,
@NotNull CompileContextEx compileContext,
@NotNull BackendCompiler compiler, TranslatingCompiler.OutputSink sink) {
myChunk = chunk;
myProject = project;
myCompiler = compiler;
myCompileContext = compileContext;
myFilesToCompile = filesToCompile;
myFilesToRecompile = new HashSet<VirtualFile>(filesToCompile);
mySink = sink;
myProjectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
mySuccesfullyCompiledJavaFiles = new HashSet<VirtualFile>(filesToCompile.size());
}
public void compile() throws CompilerException, CacheCorruptedException {
try {
if (!myFilesToCompile.isEmpty()) {
final Map<Module, List<VirtualFile>> moduleToFilesMap = buildModuleToFilesMap(myFilesToCompile);
myProcessedFilesCount = 0;
try {
// todo: need special ModuleChunk to be able to filter sources
final ModuleChunk chunk = new ModuleChunk(myCompileContext, myChunk, moduleToFilesMap);
// TODO: do we really need this for annot. processors?
//runTransformingCompilers(chunk);
setPresentableNameFor(chunk);
// assuming output dir pointing to source-generated output dir
final List<OutputDir> outs = getOutputDirsToCompileTo(chunk);
for (final OutputDir outputDir : outs) {
// todo: proper filtering
chunk.setSourcesFilter(outputDir.getKind());
doCompile(chunk, outputDir.getPath());
}
}
catch (IOException e) {
throw new CompilerException(e.getMessage(), e);
}
}
}
catch (SecurityException e) {
throw new CompilerException(CompilerBundle.message("error.compiler.process.not.started", e.getMessage()), e);
}
catch (IllegalArgumentException e) {
throw new CompilerException(e.getMessage(), e);
}
finally {
CompilerUtil.logDuration(myCompiler.getId() + " running", myCompilationDuration);
}
// do not update caches if cancelled because there is a chance that they will be incomplete
myFilesToRecompile.removeAll(mySuccesfullyCompiledJavaFiles);
if (myFilesToRecompile.size() > 0) {
mySink.add(null, Collections.<TranslatingCompiler.OutputItem>emptyList(), myFilesToRecompile.toArray(new VirtualFile[myFilesToRecompile.size()]));
}
}
private Map<Module, List<VirtualFile>> buildModuleToFilesMap(final List<VirtualFile> filesToCompile) {
if (myChunk.getNodes().size() == 1) {
return Collections.singletonMap(myChunk.getNodes().iterator().next(), Collections.unmodifiableList(filesToCompile));
}
return CompilerUtil.buildModuleToFilesMap(myCompileContext, filesToCompile);
}
private void setPresentableNameFor(final ModuleChunk chunk) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final Module[] modules = chunk.getModules();
StringBuilder moduleName = new StringBuilder(Math.min(128, modules.length * 8));
for (int idx = 0; idx < modules.length; idx++) {
final Module module = modules[idx];
if (idx > 0) {
moduleName.append(", ");
}
moduleName.append(module.getName());
if (moduleName.length() > 128 && idx + 1 < modules.length /*name is already too long and seems to grow longer*/) {
moduleName.append("...");
break;
}
}
myModuleName = moduleName.toString();
}
});
}
private List<OutputDir> getOutputDirsToCompileTo(ModuleChunk chunk) throws IOException {
// todo
return Collections.emptyList();
}
private final Object lock = new Object();
private class SynchedCompilerParsing extends CompilerParsingThread {
private SynchedCompilerParsing(Process process, final CompileContext context, OutputParser outputParser, boolean readErrorStream,
boolean trimLines) {
super(process, outputParser, readErrorStream, trimLines,context);
}
public void setProgressText(String text) {
synchronized (lock) {
super.setProgressText(text);
}
}
public void message(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum) {
synchronized (lock) {
super.message(category, message, url, lineNum, columnNum);
}
}
public void fileProcessed(String path) {
synchronized (lock) {
sourceFileProcessed();
}
}
}
private void doCompile(@NotNull final ModuleChunk chunk, @NotNull String outputDir) throws IOException {
myCompileContext.getProgressIndicator().checkCanceled();
if (ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
public Boolean compute() {
return chunk.getFilesToCompile().isEmpty() ? Boolean.TRUE : Boolean.FALSE;
}
}).booleanValue()) {
return; // should not invoke javac with empty sources list
}
ModuleType moduleType = chunk.getModules()[0].getModuleType();
if (!(chunk.getJdk().getSdkType() instanceof JavaSdkType) &&
!(moduleType instanceof JavaModuleType || moduleType.createModuleBuilder() instanceof JavaModuleBuilder)) {
// TODO
// don't try to compile non-java type module
return;
}
int exitValue = 0;
try {
Process process = myCompiler.launchProcess(chunk, outputDir, myCompileContext);
final long compilationStart = System.currentTimeMillis();
OutputParser errorParser = myCompiler.createErrorParser(outputDir, process);
CompilerParsingThread errorParsingThread = errorParser == null
? null
: new SynchedCompilerParsing(process, myCompileContext, errorParser, true, errorParser.isTrimLines());
Future<?> errorParsingThreadFuture = null;
if (errorParsingThread != null) {
errorParsingThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(errorParsingThread);
}
OutputParser outputParser = myCompiler.createOutputParser(outputDir);
CompilerParsingThread outputParsingThread = outputParser == null
? null
: new SynchedCompilerParsing(process, myCompileContext, outputParser, false, outputParser.isTrimLines());
Future<?> outputParsingThreadFuture = null;
if (outputParsingThread != null) {
outputParsingThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(outputParsingThread);
}
try {
exitValue = process.waitFor();
}
catch (InterruptedException e) {
process.destroy();
exitValue = process.exitValue();
}
finally {
myCompilationDuration += (System.currentTimeMillis() - compilationStart);
if (errorParsingThread != null) {
errorParsingThread.setProcessTerminated(true);
}
if (outputParsingThread != null) {
outputParsingThread.setProcessTerminated(true);
}
joinThread(errorParsingThreadFuture);
joinThread(outputParsingThreadFuture);
registerParsingException(outputParsingThread);
registerParsingException(errorParsingThread);
assert outputParsingThread == null || !outputParsingThread.processing;
assert errorParsingThread == null || !errorParsingThread.processing;
}
}
finally {
compileFinished(exitValue, chunk, outputDir);
myModuleName = null;
}
}
private static void joinThread(final Future<?> threadFuture) {
if (threadFuture != null) {
try {
threadFuture.get();
}
catch (InterruptedException ignored) {
}
catch(ExecutionException ignored) {
}
}
}
private void registerParsingException(final CompilerParsingThread outputParsingThread) {
Throwable error = outputParsingThread == null ? null : outputParsingThread.getError();
if (error != null) {
String message = error.getMessage();
if (error instanceof CacheCorruptedException) {
myCompileContext.requestRebuildNextTime(message);
}
else {
myCompileContext.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
}
}
}
private void compileFinished(int exitValue, final ModuleChunk chunk, final String outputDir) {
if (exitValue != 0 && !myCompileContext.getProgressIndicator().isCanceled() &&
myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0) {
myCompileContext.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("error.compiler.internal.error", exitValue), null, -1, -1);
}
myCompiler.compileFinished();
try {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final String outputDirPath = outputDir.replace(File.separatorChar, '/');
for (final Module module : chunk.getModules()) {
for (final VirtualFile root : chunk.getSourceRoots(module)) {
final String packagePrefix = myProjectFileIndex.getPackageNameByDirectory(root);
if (LOG.isDebugEnabled()) {
LOG.debug("Building output items for " + root.getPresentableUrl() + "; output dir = " + outputDirPath + "; packagePrefix = \"" + packagePrefix + "\"");
}
}
}
}
});
}
finally {
// todo: refresh dirs her
}
}
private volatile int myProcessedFilesCount = 0;
private volatile int myClassesCount = 0;
private volatile String myModuleName = null;
private void sourceFileProcessed() {
myProcessedFilesCount++;
updateStatistics();
}
private void updateStatistics() {
final String msg;
String moduleName = myModuleName;
if (moduleName != null) {
msg = CompilerBundle.message("statistics.files.classes.module", myProcessedFilesCount, myClassesCount, moduleName);
}
else {
msg = CompilerBundle.message("statistics.files.classes", myProcessedFilesCount, myClassesCount);
}
myCompileContext.getProgressIndicator().setText2(msg);
}
/*
private void runTransformingCompilers(final ModuleChunk chunk) {
final JavaSourceTransformingCompiler[] transformers =
CompilerManager.getInstance(myProject).getCompilers(JavaSourceTransformingCompiler.class);
if (transformers.length == 0) {
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Running transforming compilers...");
}
final Module[] modules = chunk.getModules();
for (final JavaSourceTransformingCompiler transformer : transformers) {
final Map<VirtualFile, VirtualFile> originalToCopyFileMap = new HashMap<VirtualFile, VirtualFile>();
final Application application = ApplicationManager.getApplication();
application.invokeAndWait(new Runnable() {
public void run() {
for (final Module module : modules) {
List<VirtualFile> filesToCompile = chunk.getFilesToCompile(module);
for (final VirtualFile file : filesToCompile) {
if (transformer.isTransformable(file)) {
application.runWriteAction(new Runnable() {
public void run() {
try {
VirtualFile fileCopy = createFileCopy(getTempDir(module), file);
originalToCopyFileMap.put(file, fileCopy);
}
catch (IOException e) {
// skip it
}
}
});
}
}
}
}
}, myCompileContext.getProgressIndicator().getModalityState());
// do actual transform
for (final Module module : modules) {
final List<VirtualFile> filesToCompile = chunk.getFilesToCompile(module);
for (int j = 0; j < filesToCompile.size(); j++) {
final VirtualFile file = filesToCompile.get(j);
VirtualFile fileCopy = originalToCopyFileMap.get(file);
if (fileCopy != null) {
final boolean ok = transformer.transform(myCompileContext, fileCopy, file);
if (ok) {
chunk.substituteWithTransformedVersion(module, j, fileCopy);
}
}
}
}
}
}
private VirtualFile createFileCopy(VirtualFile tempDir, final VirtualFile file) throws IOException {
final String fileName = file.getName();
if (tempDir.findChild(fileName) != null) {
int idx = 0;
while (true) {
//noinspection HardCodedStringLiteral
final String dirName = "dir" + idx++;
final VirtualFile dir = tempDir.findChild(dirName);
if (dir == null) {
tempDir = tempDir.createChildDirectory(this, dirName);
break;
}
if (dir.findChild(fileName) == null) {
tempDir = dir;
break;
}
}
}
return VfsUtil.copyFile(this, file, tempDir);
}
*/
}
@@ -237,7 +237,7 @@ public class ModuleChunk extends Chunk<Module> {
return convertToStringPath(cpFiles);
}
private String convertToStringPath(final OrderedSet<VirtualFile> cpFiles) {
private static String convertToStringPath(final OrderedSet<VirtualFile> cpFiles) {
final StringBuilder classpathBuffer = StringBuilderSpinAllocator.alloc();
try {
for (final VirtualFile file : cpFiles) {
@@ -310,9 +310,6 @@ public class JavacCompiler extends ExternalCompiler {
}
}
}
//additionalOptions.add("-processor");
//additionalOptions.add("CheckNamesProcessor");
//additionalOptions.add("org.apache.openjpa.persistence.meta.AnnotationProcessor6");
}
while (tokenizer.hasMoreTokens()) {
@NonNls String token = tokenizer.nextToken();
@@ -118,6 +118,10 @@ public class DependencyCache {
myTraverseRoots.clear();
}
public boolean hasUnprocessedTraverseRoots() {
return !myTraverseRoots.isEmpty();
}
public void markSourceRemoved(int qName) {
myClassesWithSourceRemoved.add(qName);
}
@@ -1,7 +1,6 @@
package com.intellij.compiler.options;
import com.intellij.compiler.CompilerConfiguration;
import com.intellij.ide.util.ElementsChooser;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.module.Module;
@@ -16,7 +15,6 @@ import com.intellij.ui.TableUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.ItemRemovable;
import com.intellij.util.ui.Table;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.TitledBorder;
@@ -38,7 +36,7 @@ import java.util.*;
* Date: Oct 5, 2009
*/
public class AnnotationProcessorsConfigurable implements Configurable{
private ElementsChooser<Module> myModulesChooser;
private ProcessedModulesChooser myModulesChooser;
private final Project myProject;
private JRadioButton myRbClasspath;
private JRadioButton myRbProcessorsPath;
@@ -107,15 +105,7 @@ public class AnnotationProcessorsConfigurable implements Configurable{
processorTablePanel.add(buttons, BorderLayout.EAST);
processorTablePanel.setPreferredSize(new Dimension(processorTablePanel.getPreferredSize().width, 50));
myModulesChooser = new ElementsChooser<Module>(true) {
protected String getItemText(@NotNull Module module) {
return module.getName() + " (" + FileUtil.toSystemDependentName(module.getModuleFilePath()) + ")";
}
protected Icon getItemIcon(Module module) {
return module.getModuleType().getNodeIcon(false);
}
};
myModulesChooser = new ProcessedModulesChooser();
myModulesChooser.setBorder(BorderFactory.createTitledBorder("Processed Modules"));
mainPanel.add(myCbEnableProcessing, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0));
@@ -0,0 +1,484 @@
/*
* 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.options;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.SpeedSearchBase;
import com.intellij.ui.TableUtil;
import com.intellij.util.ui.Table;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.*;
import java.util.List;
public class ProcessedModulesChooser extends JPanel {
private Table myTable = null;
private MyTableModel myTableModel = null;
private boolean myColorUnmarkedElements = true;
private final Map<Module, Boolean> myDisabledMap = new HashMap<Module, Boolean>();
public ProcessedModulesChooser() {
super(new BorderLayout());
myTableModel = new MyTableModel(true);
myTable = new Table(myTableModel);
myTable.setShowGrid(false);
myTable.setIntercellSpacing(new Dimension(0, 0));
myTable.setTableHeader(null);
myTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
myTable.setColumnSelectionAllowed(false);
JScrollPane pane = ScrollPaneFactory.createScrollPane(myTable);
pane.setPreferredSize(new Dimension(100, 155));
int width = new JCheckBox().getPreferredSize().width;
TableColumnModel columnModel = myTable.getColumnModel();
TableColumn checkMarkColumn = columnModel.getColumn(myTableModel.CHECK_MARK_COLUM_INDEX);
checkMarkColumn.setPreferredWidth(width);
checkMarkColumn.setMaxWidth(width);
checkMarkColumn.setCellRenderer(new CheckMarkColumnCellRenderer(myTable.getDefaultRenderer(Boolean.class)));
columnModel.getColumn(myTableModel.ELEMENT_COLUMN_INDEX).setCellRenderer(new MyElementColumnCellRenderer());
add(pane, BorderLayout.CENTER);
myTable.registerKeyboardAction(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
final int[] selectedRows = myTable.getSelectedRows();
boolean currentlyMarked = true;
for (int selectedRow : selectedRows) {
currentlyMarked = myTableModel.isElementMarked(selectedRow);
if (!currentlyMarked) {
break;
}
}
myTableModel.setMarked(selectedRows, !currentlyMarked);
}
},
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
JComponent.WHEN_FOCUSED
);
final SpeedSearchBase<Table> speedSearch = new SpeedSearchBase<Table>(myTable) {
public int getSelectedIndex() {
return myTable.getSelectedRow();
}
public Object[] getAllElements() {
final int count = myTableModel.getRowCount();
Object[] elements = new Object[count];
for (int idx = 0; idx < count; idx++) {
elements[idx] = myTableModel.getElementAt(idx);
}
return elements;
}
public String getElementText(Object element) {
return ((Module)element).getName() + " (" + FileUtil.toSystemDependentName(((Module)element).getModuleFilePath()) + ")";
}
public void selectElement(Object element, String selectedText) {
final int count = myTableModel.getRowCount();
for (int row = 0; row < count; row++) {
if (element.equals(myTableModel.getElementAt(row))) {
myTable.getSelectionModel().setSelectionInterval(row, row);
TableUtil.scrollSelectionToVisible(myTable);
break;
}
}
}
};
speedSearch.setComparator(new SpeedSearchBase.SpeedSearchComparator(false));
}
public void refresh() {
myTableModel.fireTableDataChanged();
}
public void refresh(Module element) {
final int row = myTableModel.getElementRow(element);
if (row >= 0) {
myTableModel.fireTableRowsUpdated(row, row);
}
}
private int[] mySavedSelection = null;
public void saveSelection() {
mySavedSelection = myTable.getSelectedRows();
}
public void restoreSelection() {
if (mySavedSelection != null) {
TableUtil.selectRows(myTable, mySavedSelection);
mySavedSelection = null;
}
}
public void addElement(Module element, final boolean isMarked) {
myTableModel.addElement(element, isMarked);
selectRow(myTableModel.getRowCount() - 1);
myTable.requestFocus();
}
public boolean isElementMarked(Module element) {
final int elementRow = myTableModel.getElementRow(element);
return myTableModel.isElementMarked(elementRow);
}
public void setElementMarked(Module element, boolean marked) {
final int elementRow = myTableModel.getElementRow(element);
myTableModel.setMarked(elementRow, marked);
}
public void removeElement(Module element) {
final int elementRow = myTableModel.getElementRow(element);
if (elementRow < 0) {
return; // no such element
}
final boolean wasSelected = myTable.getSelectionModel().isSelectedIndex(elementRow);
myTableModel.removeElement(element);
if (wasSelected) {
final int rowCount = myTableModel.getRowCount();
if (rowCount > 0) {
selectRow(elementRow % rowCount);
}
else {
myTable.getSelectionModel().clearSelection();
}
}
myTable.requestFocus();
}
public void removeAllElements() {
myTableModel.removeAllElements();
myTable.getSelectionModel().clearSelection();
}
private void selectRow(final int row) {
myTable.getSelectionModel().setSelectionInterval(row, row);
myTable.scrollRectToVisible(myTable.getCellRect(row, 0, true));
}
@Nullable
public Module getSelectedElement() {
final int selectedRow = getSelectedElementRow();
return selectedRow < 0? null : myTableModel.getElementAt(selectedRow);
}
public int getSelectedElementRow() {
return myTable.getSelectedRow();
}
public List<Module> getSelectedElements() {
final List<Module> elements = new ArrayList<Module>();
final int[] selectedRows = myTable.getSelectedRows();
for (int selectedRow : selectedRows) {
if (selectedRow < 0) {
continue;
}
elements.add(myTableModel.getElementAt(selectedRow));
}
return elements;
}
public void selectElements(Collection<? extends Module> elements) {
if (elements.size() == 0) {
myTable.clearSelection();
return;
}
final int[] rows = getElementsRows(elements);
TableUtil.selectRows(myTable, rows);
TableUtil.scrollSelectionToVisible(myTable);
myTable.requestFocus();
}
private int[] getElementsRows(final Collection<? extends Module> elements) {
final int[] rows = new int[elements.size()];
int index = 0;
for (final Module element : elements) {
rows[index++] = myTableModel.getElementRow(element);
}
return rows;
}
public void markElements(Collection<Module> elements) {
myTableModel.setMarked(getElementsRows(elements), true);
}
public List<Module> getMarkedElements() {
final int count = myTableModel.getRowCount();
List<Module> elements = new ArrayList<Module>();
for (int idx = 0; idx < count; idx++) {
final Module element = myTableModel.getElementAt(idx);
if (myTableModel.isElementMarked(idx)) {
elements.add(element);
}
}
return elements;
}
public void sort(Comparator<Module> comparator) {
myTableModel.sort(comparator);
}
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
myTable.setRowSelectionAllowed(enabled);
myTableModel.fireTableDataChanged();
}
public void stopEditing() {
TableCellEditor editor = myTable.getCellEditor();
if (editor != null) {
editor.stopCellEditing();
}
}
public JComponent getComponent() {
return myTable;
}
public void clear() {
myTableModel.clear();
}
public int getElementCount() {
return myTableModel.getRowCount();
}
public Module getElementAt(int row) {
return myTableModel.getElementAt(row);
}
public void disableElement(Module element) {
myDisabledMap.put(element, Boolean.TRUE);
}
private final class MyTableModel extends AbstractTableModel {
private final List<Module> myElements = new ArrayList<Module>();
private final Map<Module, Boolean> myMarkedMap = new HashMap<Module, Boolean>();
public final int CHECK_MARK_COLUM_INDEX;
public final int ELEMENT_COLUMN_INDEX;
private final boolean myElementsCanBeMarked;
public MyTableModel(final boolean elementsCanBeMarked) {
myElementsCanBeMarked = elementsCanBeMarked;
if (elementsCanBeMarked) {
CHECK_MARK_COLUM_INDEX = 0;
ELEMENT_COLUMN_INDEX = 1;
}
else {
CHECK_MARK_COLUM_INDEX = -1;
ELEMENT_COLUMN_INDEX = 0;
}
}
public void sort(Comparator<Module> comparator) {
Collections.sort(myElements, comparator);
fireTableDataChanged();
}
public Module getElementAt(int index) {
return myElements.get(index);
}
public boolean isElementMarked(int index) {
final Module element = myElements.get(index);
final Boolean isMarked = myMarkedMap.get(element);
return isMarked.booleanValue();
}
void addElement(Module element, boolean isMarked) {
myElements.add(element);
myMarkedMap.put(element, isMarked? Boolean.TRUE : Boolean.FALSE);
int row = myElements.size() - 1;
fireTableRowsInserted(row, row);
}
void addElements(List<Module> elements, boolean isMarked) {
if (elements == null || elements.size() == 0) {
return;
}
for (final Module element : elements) {
myElements.add(element);
myMarkedMap.put(element, isMarked ? Boolean.TRUE : Boolean.FALSE);
}
fireTableRowsInserted(myElements.size() - elements.size(), myElements.size() - 1);
}
public void removeElement(Module element) {
final boolean reallyRemoved = myElements.remove(element);
if (reallyRemoved) {
myMarkedMap.remove(element);
fireTableDataChanged();
}
}
public void changeElementRow(Module element, int row) {
final boolean reallyRemoved = myElements.remove(element);
if (reallyRemoved) {
myElements.add(row, element);
fireTableDataChanged();
}
}
public int getElementRow(Module element) {
return myElements.indexOf(element);
}
public void removeAllElements() {
myElements.clear();
fireTableDataChanged();
}
public void removeRows(int[] rows) {
final List<Module> toRemove = new ArrayList<Module>();
for (int row : rows) {
final Module element = myElements.get(row);
toRemove.add(element);
myMarkedMap.remove(element);
}
myElements.removeAll(toRemove);
fireTableDataChanged();
}
public int getRowCount() {
return myElements.size();
}
public int getColumnCount() {
return myElementsCanBeMarked? 2 : 1;
}
@Nullable
public Object getValueAt(int rowIndex, int columnIndex) {
Module element = myElements.get(rowIndex);
if (columnIndex == ELEMENT_COLUMN_INDEX) {
return element;
}
if (columnIndex == CHECK_MARK_COLUM_INDEX) {
return myMarkedMap.get(element);
}
return null;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == CHECK_MARK_COLUM_INDEX) {
setMarked(rowIndex, ((Boolean)aValue).booleanValue());
}
}
private void setMarked(int rowIndex, final boolean marked) {
final Module element = myElements.get(rowIndex);
final Boolean newValue = marked? Boolean.TRUE : Boolean.FALSE;
myMarkedMap.put(element, newValue);
fireTableRowsUpdated(rowIndex, rowIndex);
}
private void setMarked(int[] rows, final boolean marked) {
if (rows == null || rows.length == 0) {
return;
}
int firstRow = Integer.MAX_VALUE;
int lastRow = Integer.MIN_VALUE;
final Boolean newValue = marked? Boolean.TRUE : Boolean.FALSE;
for (final int row : rows) {
final Module element = myElements.get(row);
myMarkedMap.put(element, newValue);
firstRow = Math.min(firstRow, row);
lastRow = Math.max(lastRow, row);
}
fireTableRowsUpdated(firstRow, lastRow);
}
public Class getColumnClass(int columnIndex) {
if (columnIndex == CHECK_MARK_COLUM_INDEX) {
return Boolean.class;
}
return super.getColumnClass(columnIndex);
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (!ProcessedModulesChooser.this.isEnabled() || columnIndex != CHECK_MARK_COLUM_INDEX) {
return false;
}
final Module o = (Module)getValueAt(rowIndex, ELEMENT_COLUMN_INDEX);
return myDisabledMap.get(o) == null;
}
public void clear() {
myElements.clear();
myMarkedMap.clear();
fireTableDataChanged();
}
}
private class MyElementColumnCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
final Color color = UIUtil.getTableFocusCellBackground();
Component component;
Module module = (Module)value;
try {
UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, table.getSelectionBackground());
component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
setText(module != null ? module.getName() + " (" + FileUtil.toSystemDependentName(module.getModuleFilePath()) + ")" : "");
if (component instanceof JLabel) {
((JLabel)component).setBorder(noFocusBorder);
}
}
finally {
UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, color);
}
final MyTableModel model = (MyTableModel)table.getModel();
component.setEnabled(ProcessedModulesChooser.this.isEnabled() && (myColorUnmarkedElements? model.isElementMarked(row) : true));
if (component instanceof JLabel) {
final Icon icon = module != null ? module.getModuleType().getNodeIcon(false) : null;
JLabel label = (JLabel)component;
label.setIcon(icon);
label.setDisabledIcon(icon);
}
component.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
return component;
}
}
private class CheckMarkColumnCellRenderer implements TableCellRenderer {
private final TableCellRenderer myDelegate;
public CheckMarkColumnCellRenderer(TableCellRenderer delegate) {
myDelegate = delegate;
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component component = myDelegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
component.setEnabled(ProcessedModulesChooser.this.isEnabled());
if (component instanceof JComponent) {
((JComponent)component).setBorder(null);
}
return component;
}
}
}
@@ -19,6 +19,7 @@ package com.intellij.compiler;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
@@ -59,4 +60,13 @@ public abstract class CompilerConfiguration {
public abstract Set<Module> getExcludedModules();
public abstract void setExcludedModules(Collection<Module> modules);
public abstract boolean isStoreGenerateSourcesUnderModuleContent(Module module);
public abstract void setStoreGenerateSourcesUnderModuleContent(boolean storeGenerateSourcesUnderModuleContent);
@NotNull
public abstract String getGeneratedDirName();
public abstract void setGeneratedDirName(String generatedDirName);
}
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.compiler;
import com.intellij.compiler.CompilerConfiguration;
import com.intellij.ide.highlighter.ProjectFileType;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
@@ -23,6 +24,8 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.CompilerModuleExtension;
import com.intellij.openapi.roots.CompilerProjectExtension;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -33,6 +36,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
/**
@@ -41,6 +46,11 @@ import java.util.Locale;
public class CompilerPaths {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.compiler.CompilerPaths");
private static volatile String ourSystemPath;
private static final Comparator<String> URLS_COMPARATOR = new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
/**
* Returns a directory
@@ -180,6 +190,27 @@ public class CompilerPaths {
return outPathUrl != null? VirtualFileManager.extractPath(outPathUrl) : null;
}
public static String getAnnotationProcessorsGenerationPath(Module module) {
final CompilerConfiguration config = CompilerConfiguration.getInstance(module.getProject());
if (config.isStoreGenerateSourcesUnderModuleContent(module)) {
final String[] roots = ModuleRootManager.getInstance(module).getContentRootUrls();
if (roots.length == 0) {
return null;
}
if (roots.length > 1) {
Arrays.sort(roots, URLS_COMPARATOR);
}
return VirtualFileManager.extractPath(roots[0]) + "/" + config.getGeneratedDirName();
}
final String url = CompilerProjectExtension.getInstance(module.getProject()).getCompilerOutputUrl();
if (url == null) {
return null;
}
return VirtualFileManager.extractPath(url) + "/generated/" + module.getName().toLowerCase();
}
@NonNls
public static String getGenerationOutputPath(IntermediateOutputCompiler compiler, Module module, final boolean forTestSources) {
final String generatedCompilerDirectoryPath = getGeneratedDataDirectory(module.getProject(), compiler).getPath();
@@ -21,11 +21,11 @@ import com.intellij.ide.util.projectWizard.JdkChooserPanel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.JdkOrderEntry;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.projectRoots.Sdk;
import org.jetbrains.annotations.Nullable;
/**
@@ -38,6 +38,15 @@ public class IdeaProjectSettingsService extends ProjectSettingsService {
myProject = project;
}
public void openProjectSettings() {
final ProjectStructureConfigurable config = ProjectStructureConfigurable.getInstance(myProject);
ShowSettingsUtil.getInstance().editConfigurable(myProject, config, new Runnable() {
public void run() {
config.selectProjectGeneralSettings(true);
}
});
}
public void openModuleSettings(final Module module) {
ModulesConfigurator.showDialog(myProject, module.getName(), null, false);
}
@@ -338,6 +338,11 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se
Place.queryFurther(mySelectedConfigurable, place);
}
public ActionCallback selectProjectGeneralSettings(final boolean requestFocus) {
Place place = new Place().putPath(CATEGORY, myProjectConfig);
return navigateTo(place, requestFocus);
}
public ActionCallback select(@Nullable final String moduleToSelect, String tab, final boolean requestFocus) {
Place place = new Place().putPath(CATEGORY, myModulesConfig);
if (moduleToSelect != null) {