From 9bed8ccc3bb6cd558993fa3368befae318eda8a1 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Wed, 30 May 2012 11:40:39 +0200 Subject: [PATCH] - annotation processors profiles - out-of-process build: supported annotation processing configurable via profiles --- .../compiler/CompilerConfigurationImpl.java | 420 ++++++++-------- .../compiler/ProcessorConfigProfile.java | 210 ++++++++ .../actions/ProcessAnnotationsAction.java | 10 +- .../intellij/compiler/impl/CompileDriver.java | 20 +- .../AnnotationProcessingCompiler.java | 2 +- .../javaCompiler/javac/JavacCompiler.java | 33 +- .../AnnotationProcessorsConfigurable.java | 355 +------------- .../options/AnnotationProcessorsPanel.java | 280 +++++++---- .../options/ProcessorProfilePanel.java | 454 ++++++++++++++++++ .../AnnotationProcessingConfiguration.java | 44 ++ .../compiler/CompilerConfiguration.java | 35 +- .../openapi/compiler/CompilerPaths.java | 4 +- .../jps/incremental/CompileContext.java | 27 ++ .../jps/incremental/java/JavaBuilder.java | 63 ++- .../org/jetbrains/jps/javac/JavacMain.java | 20 +- .../jps/CompilerConfiguration.groovy | 10 +- .../src/org/jetbrains/jps/ProjectPaths.java | 32 ++ .../jps/idea/IdeaProjectLoader.groovy | 42 +- .../compilerConfiguration.ipr | 14 +- .../.idea/compiler.xml | 14 +- .../jps/CompilerConfigurationTest.groovy | 11 +- 21 files changed, 1374 insertions(+), 726 deletions(-) create mode 100644 java/compiler/impl/src/com/intellij/compiler/ProcessorConfigProfile.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/options/ProcessorProfilePanel.java create mode 100644 java/compiler/openapi/src/com/intellij/compiler/AnnotationProcessingConfiguration.java diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java index 6ffe1062d18c..0eb6af979ed6 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java @@ -52,7 +52,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.HashMap; import org.apache.oro.text.regex.*; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -74,6 +73,12 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.CompilerConfiguration"); @NonNls public static final String TESTS_EXTERNAL_COMPILER_HOME_PROPERTY_NAME = "tests.external.compiler.home"; public static final int DEPENDENCY_FORMAT_VERSION = 55; + private static final Comparator ALPHA_COMPARATOR = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.compareToIgnoreCase(o2); + } + }; @SuppressWarnings({"WeakerAccess"}) public String DEFAULT_COMPILER; @NotNull private BackendCompiler myDefaultJavaCompiler; @@ -96,15 +101,14 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements { loadDefaultWildcardPatterns(); } - - private boolean myEnableAnnotationProcessors = false; - private final Map myProcessorsMap = new HashMap(); // map: AnnotationProcessorName -> options - private boolean myObtainProcessorsFromClasspath = true; - private String myProcessorPath = ""; - private final Map myProcessedModules = new HashMap(); - private final Map myModuleNames = new HashMap(); private boolean myAddNotNullAssertions = true; + private final ProcessorConfigProfile myDefaultProcessorsProfile = new ProcessorConfigProfile("Default"); + private final List myModuleProcessorProfiles = new ArrayList(); + + // the map is calculated by module processor profiles list for faster access to module settings + private Map myProcessorsProfilesMap = null; + @Nullable private String myBytecodeTargetLevel = null; // null means compiler default private final Map myModuleBytecodeTarget = new java.util.HashMap(); @@ -116,16 +120,11 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements Disposer.register(project, myExcludedEntriesConfiguration); project.getMessageBus().connect(project).subscribe(ProjectTopics.MODULES, new ModuleAdapter() { public void beforeModuleRemoved(Project project, Module module) { - myProcessedModules.remove(module); - myModuleNames.remove(module.getName()); + getAnnotationProcessingConfiguration(module).removeModuleName(module.getName()); } public void moduleAdded(Project project, Module module) { - final String moduleName = module.getName(); - if (myModuleNames.containsKey(moduleName)) { - final String dirName = myModuleNames.remove(moduleName); - myProcessedModules.put(module, dirName); - } + myProcessorsProfilesMap = null; // clear cache } }); } @@ -376,55 +375,62 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements myAddNotNullAssertions = enabled; } + @NotNull + public ProcessorConfigProfile getDefaultProcessorProfile() { + return myDefaultProcessorsProfile; + } + + public void setDefaultProcessorProfile(ProcessorConfigProfile profile) { + myDefaultProcessorsProfile.initFrom(profile); + } + + @NotNull + public List getModuleProcessorProfiles() { + return myModuleProcessorProfiles; + } + + public void setModuleProcessorProfiles(Collection moduleProfiles) { + myModuleProcessorProfiles.clear(); + myModuleProcessorProfiles.addAll(moduleProfiles); + } + + @Override + @NotNull + public ProcessorConfigProfile getAnnotationProcessingConfiguration(Module module) { + Map map = myProcessorsProfilesMap; + if (map == null) { + map = new HashMap(); + final Map namesMap = new HashMap(); + for (Module m : ModuleManager.getInstance(module.getProject()).getModules()) { + namesMap.put(m.getName(), m); + } + if (!namesMap.isEmpty()) { + for (ProcessorConfigProfile profile : myModuleProcessorProfiles) { + for (String name : profile.getModuleNames()) { + final Module mod = namesMap.get(name); + if (mod != null) { + map.put(mod, profile); + } + } + } + } + myProcessorsProfilesMap = map; + } + final ProcessorConfigProfile profile = map.get(module); + return profile != null? profile : myDefaultProcessorsProfile; + } + + @Override public boolean isAnnotationProcessorsEnabled() { - return myEnableAnnotationProcessors; - } - - public void setAnnotationProcessorsEnabled(boolean enableAnnotationProcessors) { - myEnableAnnotationProcessors = enableAnnotationProcessors; - } - - public boolean isObtainProcessorsFromClasspath() { - return myObtainProcessorsFromClasspath; - } - - public void setObtainProcessorsFromClasspath(boolean obtainProcessorsFromClasspath) { - myObtainProcessorsFromClasspath = obtainProcessorsFromClasspath; - } - - public String getProcessorPath() { - return myProcessorPath; - } - - public void setProcessorsPath(String processorsPath) { - myProcessorPath = processorsPath; - } - - public Map getAnnotationProcessorsMap() { - return Collections.unmodifiableMap(myProcessorsMap); - } - - public void setAnnotationProcessorsMap(Map map) { - myProcessorsMap.clear(); - myProcessorsMap.putAll(map); - } - - public void setAnotationProcessedModules(Map modules) { - myProcessedModules.clear(); - myModuleNames.clear(); - myProcessedModules.putAll(modules); - } - - public Map getAnotationProcessedModules() { - return Collections.unmodifiableMap(myProcessedModules); - } - - public boolean isAnnotationProcessingEnabled(Module module) { - return myProcessedModules.containsKey(module); - } - - public String getGeneratedSourceDirName(Module module) { - return myProcessedModules.get(module); + if (myDefaultProcessorsProfile.isEnabled()) { + return true; + } + for (ProcessorConfigProfile profile : myModuleProcessorProfiles) { + if (profile.isEnabled()) { + return true; + } + } + return false; } private void addWildcardResourcePattern(@NonNls final String wildcardPattern) throws MalformedPatternException { @@ -579,21 +585,26 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements } // property names - @NonNls private static final String EXCLUDE_FROM_COMPILE = "excludeFromCompile"; - @NonNls private static final String RESOURCE_EXTENSIONS = "resourceExtensions"; - @NonNls private static final String ANNOTATION_PROCESSING = "annotationProcessing"; - @NonNls private static final String BYTECODE_TARGET_LEVEL = "bytecodeTargetLevel"; - @NonNls private static final String WILDCARD_RESOURCE_PATTERNS = "wildcardResourcePatterns"; - @NonNls private static final String ENTRY = "entry"; - @NonNls private static final String NAME = "name"; - @NonNls private static final String ADD_NOTNULL_ASSERTIONS = "addNotNullAssertions"; + private static final String EXCLUDE_FROM_COMPILE = "excludeFromCompile"; + private static final String RESOURCE_EXTENSIONS = "resourceExtensions"; + private static final String ANNOTATION_PROCESSING = "annotationProcessing"; + private static final String BYTECODE_TARGET_LEVEL = "bytecodeTargetLevel"; + private static final String WILDCARD_RESOURCE_PATTERNS = "wildcardResourcePatterns"; + private static final String ADD_NOTNULL_ASSERTIONS = "addNotNullAssertions"; + private static final String ENTRY = "entry"; + private static final String NAME = "name"; + private static final String VALUE = "value"; + private static final String ENABLED = "enabled"; + private static final String OPTION = "option"; + private static final String MODULE = "module"; + public void readExternal(Element parentNode) throws InvalidDataException { DefaultJDOMExternalizer.readExternal(this, parentNode); final Element notNullAssertions = parentNode.getChild(ADD_NOTNULL_ASSERTIONS); if (notNullAssertions != null) { - myAddNotNullAssertions = Boolean.valueOf(notNullAssertions.getAttributeValue("enabled", "true")); + myAddNotNullAssertions = Boolean.valueOf(notNullAssertions.getAttributeValue(ENABLED, "true")); } Element node = parentNode.getChild(EXCLUDE_FROM_COMPILE); @@ -608,7 +619,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements for (final Object o : node.getChildren(ENTRY)) { Element element = (Element)o; String pattern = element.getAttributeValue(NAME); - if (pattern != null && !"".equals(pattern)) { + if (!StringUtil.isEmpty(pattern)) { addRegexpPattern(pattern); } } @@ -621,7 +632,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements for (final Object o : node.getChildren(ENTRY)) { final Element element = (Element)o; String pattern = element.getAttributeValue(NAME); - if (pattern != null && !"".equals(pattern)) { + if (!StringUtil.isEmpty(pattern)) { addWildcardResourcePattern(pattern); } } @@ -631,69 +642,41 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements throw new InvalidDataException(e); } + + myModuleProcessorProfiles.clear(); + myProcessorsProfilesMap = null; + final Element annotationProcessingSettings = parentNode.getChild(ANNOTATION_PROCESSING); if (annotationProcessingSettings != null) { - myEnableAnnotationProcessors = Boolean.valueOf(annotationProcessingSettings.getAttributeValue("enabled", "false")); - myObtainProcessorsFromClasspath = Boolean.valueOf(annotationProcessingSettings.getAttributeValue("useClasspath", "true")); - - final StringBuilder pathBuilder = new StringBuilder(); - for (Element pathElement : (Collection)annotationProcessingSettings.getChildren("processorPath")) { - final String path = pathElement.getAttributeValue("value"); - if (path != null) { - if (pathBuilder.length() > 0) { - pathBuilder.append(File.pathSeparator); - } - pathBuilder.append(path); + for (Object elem : annotationProcessingSettings.getChildren("profile")) { + final Element profileElement = (Element)elem; + final boolean isDefault = "true".equals(profileElement.getAttributeValue("default")); + if (isDefault) { + readProfile(profileElement, myDefaultProcessorsProfile); + } + else { + final ProcessorConfigProfile profile = new ProcessorConfigProfile(""); + readProfile(profileElement, profile); + myModuleProcessorProfiles.add(profile); } } - myProcessorPath = pathBuilder.toString(); + } - myProcessorsMap.clear(); - for (Element processorChild : (Collection)annotationProcessingSettings.getChildren("processor")) { - final String name = processorChild.getAttributeValue("name"); - final String options = processorChild.getAttributeValue("options", ""); - myProcessorsMap.put(name, options); - } - myProcessedModules.clear(); - myModuleNames.clear(); - - final Collection processed = (Collection)annotationProcessingSettings.getChildren("processModule"); - if (!processed.isEmpty()) { - final Map moduleMap = new HashMap(); - for (Module module : myModuleManager.getModules()) { - moduleMap.put(module.getName(), module); + myBytecodeTargetLevel = null; + myModuleBytecodeTarget.clear(); + final Element bytecodeTargetElement = parentNode.getChild(BYTECODE_TARGET_LEVEL); + if (bytecodeTargetElement != null) { + myBytecodeTargetLevel = bytecodeTargetElement.getAttributeValue("target"); + for (Element elem : (Collection)bytecodeTargetElement.getChildren(MODULE)) { + final String name = elem.getAttributeValue(NAME); + if (name == null) { + continue; } - for (Element moduleElement : processed) { - final String name = moduleElement.getAttributeValue("name"); - final String dirname = moduleElement.getAttributeValue("generatedDirName"); - if (name != null) { - final Module module = moduleMap.get(name); - if (module != null) { - myProcessedModules.put(module, dirname); - } - else { - myModuleNames.put(name, dirname); - } - } - } - } - - myBytecodeTargetLevel = null; - myModuleBytecodeTarget.clear(); - final Element bytecodeTargetElement = parentNode.getChild(BYTECODE_TARGET_LEVEL); - if (bytecodeTargetElement != null) { - myBytecodeTargetLevel = bytecodeTargetElement.getAttributeValue("target"); - for (Element elem : (Collection)bytecodeTargetElement.getChildren("module")) { - final String name = elem.getAttributeValue("name"); - if (name == null) { - continue; - } - final String target = elem.getAttributeValue("target"); - if (target == null) { - continue; - } - myModuleBytecodeTarget.put(name, target); + final String target = elem.getAttributeValue("target"); + if (target == null) { + continue; } + myModuleBytecodeTarget.put(name, target); } } } @@ -702,88 +685,42 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements DefaultJDOMExternalizer.writeExternal(this, parentNode); if (myAddNotNullAssertions != true) { - final Element notNullAssertions = new Element(ADD_NOTNULL_ASSERTIONS); - notNullAssertions.setAttribute("enabled", String.valueOf(myAddNotNullAssertions)); - parentNode.addContent(notNullAssertions); + addChild(parentNode, ADD_NOTNULL_ASSERTIONS).setAttribute(ENABLED, String.valueOf(myAddNotNullAssertions)); } if(myExcludedEntriesConfiguration.getExcludeEntryDescriptions().length > 0) { - Element newChild = new Element(EXCLUDE_FROM_COMPILE); - myExcludedEntriesConfiguration.writeExternal(newChild); - parentNode.addContent(newChild); + myExcludedEntriesConfiguration.writeExternal(addChild(parentNode, EXCLUDE_FROM_COMPILE)); } - final Element newChild = new Element(RESOURCE_EXTENSIONS); + final Element newChild = addChild(parentNode, RESOURCE_EXTENSIONS); for (final String pattern : getRegexpPatterns()) { - final Element entry = new Element(ENTRY); - entry.setAttribute(NAME, pattern); - newChild.addContent(entry); + addChild(newChild, ENTRY).setAttribute(NAME, pattern); } - parentNode.addContent(newChild); if (myWildcardPatternsInitialized || !myWildcardPatterns.isEmpty()) { - final Element wildcardPatterns = new Element(WILDCARD_RESOURCE_PATTERNS); + final Element wildcardPatterns = addChild(parentNode, WILDCARD_RESOURCE_PATTERNS); for (final String wildcardPattern : myWildcardPatterns) { - final Element entry = new Element(ENTRY); - entry.setAttribute(NAME, wildcardPattern); - wildcardPatterns.addContent(entry); + addChild(wildcardPatterns, ENTRY).setAttribute(NAME, wildcardPattern); } - parentNode.addContent(wildcardPatterns); } - final Element annotationProcessingSettings = new Element(ANNOTATION_PROCESSING); - parentNode.addContent(annotationProcessingSettings); - annotationProcessingSettings.setAttribute("enabled", String.valueOf(myEnableAnnotationProcessors)); - annotationProcessingSettings.setAttribute("useClasspath", String.valueOf(myObtainProcessorsFromClasspath)); - if (myProcessorPath.length() > 0) { - final StringTokenizer tokenizer = new StringTokenizer(myProcessorPath, File.pathSeparator, false); - while (tokenizer.hasMoreTokens()) { - final String path = tokenizer.nextToken(); - final Element pathElement = new Element("processorPath"); - annotationProcessingSettings.addContent(pathElement); - pathElement.setAttribute("value", path); - } - } - for (Map.Entry entry : myProcessorsMap.entrySet()) { - final Element processor = new Element("processor"); - annotationProcessingSettings.addContent(processor); - processor.setAttribute("name", entry.getKey()); - processor.setAttribute("options", entry.getValue()); - } - final List modules = new ArrayList(myProcessedModules.keySet()); - Collections.sort(modules, new Comparator() { - public int compare(Module o1, Module o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); - for (Module module : modules) { - final Element moduleElement = new Element("processModule"); - annotationProcessingSettings.addContent(moduleElement); - moduleElement.setAttribute("name", module.getName()); - final String dirName = myProcessedModules.get(module); - if (dirName != null && dirName.length() > 0) { - moduleElement.setAttribute("generatedDirName", dirName); - } + final Element annotationProcessingSettings = addChild(parentNode, ANNOTATION_PROCESSING); + writeProfile(addChild(annotationProcessingSettings, "profile").setAttribute("default", "true"), myDefaultProcessorsProfile); + for (ProcessorConfigProfile profile : myModuleProcessorProfiles) { + writeProfile(addChild(annotationProcessingSettings, "profile").setAttribute("default", "false"), profile); } if (!StringUtil.isEmpty(myBytecodeTargetLevel) || !myModuleBytecodeTarget.isEmpty()) { - final Element bytecodeTarget = new Element(BYTECODE_TARGET_LEVEL); - parentNode.addContent(bytecodeTarget); + final Element bytecodeTarget = addChild(parentNode, BYTECODE_TARGET_LEVEL); if (!StringUtil.isEmpty(myBytecodeTargetLevel)) { bytecodeTarget.setAttribute("target", myBytecodeTargetLevel); } if (!myModuleBytecodeTarget.isEmpty()) { final List moduleNames = new ArrayList(myModuleBytecodeTarget.keySet()); - Collections.sort(moduleNames, new Comparator() { - @Override - public int compare(String o1, String o2) { - return o1.compareTo(o2); - } - }); + Collections.sort(moduleNames, ALPHA_COMPARATOR); for (String name : moduleNames) { - final Element moduleElement = new Element("module"); - bytecodeTarget.addContent(moduleElement); - moduleElement.setAttribute("name", name); + final Element moduleElement = addChild(bytecodeTarget, MODULE); + moduleElement.setAttribute(NAME, name); final String value = myModuleBytecodeTarget.get(name); moduleElement.setAttribute("target", value != null? value : ""); } @@ -791,6 +728,109 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements } } + private static void readProfile(Element element, ProcessorConfigProfile profile) { + profile.setName(element.getAttributeValue(NAME, "")); + profile.setEnabled(Boolean.valueOf(element.getAttributeValue(ENABLED, "false"))); + + final Element srcOutput = element.getChild("sourceOutputDir"); + profile.setGeneratedSourcesDirectoryName(srcOutput != null? srcOutput.getAttributeValue(NAME) : null); + + profile.clearProcessorOptions(); + for (Object optionElement : element.getChildren(OPTION)) { + final Element elem = (Element)optionElement; + final String key = elem.getAttributeValue(NAME); + final String value = elem.getAttributeValue(VALUE); + if (!StringUtil.isEmptyOrSpaces(key) && value != null) { + profile.setOption(key, value); + } + } + + profile.clearProcessors(); + for (Object procElement : element.getChildren("processor")) { + final String name = ((Element)procElement).getAttributeValue(NAME); + if (StringUtil.isEmptyOrSpaces(name)) { + profile.addProcessor(name); + } + } + + final Element pathElement = element.getChild("processorPath"); + if (pathElement != null) { + profile.setObtainProcessorsFromClasspath(Boolean.parseBoolean(pathElement.getAttributeValue("useClasspath", "true"))); + final StringBuilder pathBuilder = new StringBuilder(); + for (Object entry : pathElement.getChildren(ENTRY)) { + final String path = ((Element)entry).getAttributeValue(NAME); + if (!StringUtil.isEmptyOrSpaces(path)) { + if (pathBuilder.length() > 0) { + pathBuilder.append(File.pathSeparator); + } + pathBuilder.append(FileUtil.toSystemDependentName(path)); + } + } + profile.setProcessorPath(pathBuilder.toString()); + } + + profile.clearModuleNames(); + for (Object moduleElement : element.getChildren(MODULE)) { + final String name = ((Element)moduleElement).getAttributeValue(NAME); + if (!StringUtil.isEmptyOrSpaces(name)) { + profile.addModuleName(name); + } + } + } + + private static void writeProfile(final Element element, ProcessorConfigProfile profile) { + element.setAttribute(NAME, profile.getName()); + element.setAttribute(ENABLED, Boolean.toString(profile.isEnabled())); + + final String srcDirName = profile.getGeneratedSourcesDirectoryName(); + if (srcDirName != null) { + addChild(element, "sourceOutputDir").setAttribute(NAME, srcDirName); + } + + final Map options = profile.getProcessorOptions(); + if (!options.isEmpty()) { + final List keys = new ArrayList(options.keySet()); + Collections.sort(keys, ALPHA_COMPARATOR); + for (String key : keys) { + addChild(element, OPTION).setAttribute(NAME, key).setAttribute(VALUE, options.get(key)); + } + } + + final Set processors = profile.getProcessors(); + if (!processors.isEmpty()) { + final List processorList = new ArrayList(processors); + Collections.sort(processorList, ALPHA_COMPARATOR); + for (String proc : processorList) { + addChild(element, "processor").setAttribute(NAME, proc); + } + } + + final Element pathElement = addChild(element, "processorPath").setAttribute("useClasspath", Boolean.toString(profile.isObtainProcessorsFromClasspath())); + final String path = profile.getProcessorPath(); + if (!StringUtil.isEmpty(path)) { + final StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator, false); + while (tokenizer.hasMoreTokens()) { + final String token = tokenizer.nextToken(); + addChild(pathElement, ENTRY).setAttribute(NAME, FileUtil.toSystemIndependentName(token)); + } + } + + final Set moduleNames = profile.getModuleNames(); + if (!moduleNames.isEmpty()) { + final List names = new ArrayList(moduleNames); + Collections.sort(names, ALPHA_COMPARATOR); + for (String name : names) { + addChild(element, MODULE).setAttribute(NAME, name); + } + } + } + + private static Element addChild(Element parent, final String childName) { + final Element child = new Element(childName); + parent.addContent(child); + return child; + } + @NotNull @NonNls public String getComponentName() { return "CompilerConfiguration"; diff --git a/java/compiler/impl/src/com/intellij/compiler/ProcessorConfigProfile.java b/java/compiler/impl/src/com/intellij/compiler/ProcessorConfigProfile.java new file mode 100644 index 000000000000..7922743712b8 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/ProcessorConfigProfile.java @@ -0,0 +1,210 @@ +/* + * Copyright 2000-2012 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; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Eugene Zhuravlev + * Date: 5/25/12 + */ +public class ProcessorConfigProfile implements AnnotationProcessingConfiguration { + private String myName = ""; + + private boolean myEnabled = false; + private boolean myObtainProcessorsFromClasspath = true; + private String myProcessorPath = ""; + private final Set myProcessors = new HashSet(); // empty list means all discovered + private final Map myProcessorOptions = new HashMap(); // key=value map of options + @Nullable + private String myGeneratedSourcesDirectoryName = null; // null means 'auto' + private final Set myModuleNames = new HashSet(); + + public ProcessorConfigProfile(String name) { + myName = name; + } + + public ProcessorConfigProfile(ProcessorConfigProfile profile) { + initFrom(profile); + } + + public final void initFrom(ProcessorConfigProfile other) { + myName = other.myName; + myEnabled = other.myEnabled; + myObtainProcessorsFromClasspath = other.myObtainProcessorsFromClasspath; + myProcessorPath = other.myProcessorPath; + myProcessors.clear(); + myProcessors.addAll(other.myProcessors); + myProcessorOptions.clear(); + myProcessorOptions.putAll(other.myProcessorOptions); + myGeneratedSourcesDirectoryName = other.myGeneratedSourcesDirectoryName; + myModuleNames.clear(); + myModuleNames.addAll(other.myModuleNames); + } + + public String getName() { + return myName; + } + + public void setName(String name) { + myName = name; + } + + @Override + public boolean isEnabled() { + return myEnabled; + } + + public void setEnabled(boolean enabled) { + myEnabled = enabled; + } + + @Override + @NotNull + public String getProcessorPath() { + return myProcessorPath; + } + + public void setProcessorPath(@Nullable String processorPath) { + myProcessorPath = processorPath != null? processorPath : ""; + } + + @Override + public boolean isObtainProcessorsFromClasspath() { + return myObtainProcessorsFromClasspath; + } + + public void setObtainProcessorsFromClasspath(boolean value) { + myObtainProcessorsFromClasspath = value; + } + + @Override + @Nullable + public String getGeneratedSourcesDirectoryName() { + return myGeneratedSourcesDirectoryName; + } + + public void setGeneratedSourcesDirectoryName(@Nullable String generatedSourcesDirectoryName) { + myGeneratedSourcesDirectoryName = generatedSourcesDirectoryName; + } + + @NotNull + public Set getModuleNames() { + return myModuleNames; + } + + public boolean addModuleName(String name) { + return myModuleNames.add(name); + } + + public boolean addModuleNames(Collection names) { + return myModuleNames.addAll(names); + } + + public boolean removeModuleName(String name) { + return myModuleNames.remove(name); + } + + public boolean removeModuleNames(Collection names) { + return myModuleNames.removeAll(names); + } + + public void clearModuleNames() { + myModuleNames.clear(); + } + + public void clearProcessors() { + myProcessors.clear(); + } + + public boolean addProcessor(String processor) { + return myProcessors.add(processor); + } + + public boolean removeProcessor(String processor) { + return myProcessors.remove(processor); + } + + @Override + @NotNull + public Set getProcessors() { + return Collections.unmodifiableSet(myProcessors); + } + + @Override + @NotNull + public Map getProcessorOptions() { + return Collections.unmodifiableMap(myProcessorOptions); + } + + public String setOption(String key, String value) { + return myProcessorOptions.put(key, value); + } + + @Nullable + public String getOption(String key) { + return myProcessorOptions.get(key); + } + + public void clearProcessorOptions() { + myProcessorOptions.clear(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ProcessorConfigProfile profile = (ProcessorConfigProfile)o; + + if (myEnabled != profile.myEnabled) return false; + if (myObtainProcessorsFromClasspath != profile.myObtainProcessorsFromClasspath) return false; + if (myGeneratedSourcesDirectoryName != null + ? !myGeneratedSourcesDirectoryName.equals(profile.myGeneratedSourcesDirectoryName) + : profile.myGeneratedSourcesDirectoryName != null) { + return false; + } + if (!myModuleNames.equals(profile.myModuleNames)) return false; + if (!myProcessorOptions.equals(profile.myProcessorOptions)) return false; + if (myProcessorPath != null ? !myProcessorPath.equals(profile.myProcessorPath) : profile.myProcessorPath != null) return false; + if (!myProcessors.equals(profile.myProcessors)) return false; + if (!myName.equals(profile.myName)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myName.hashCode(); + result = 31 * result + (myEnabled ? 1 : 0); + result = 31 * result + (myObtainProcessorsFromClasspath ? 1 : 0); + result = 31 * result + (myProcessorPath != null ? myProcessorPath.hashCode() : 0); + result = 31 * result + myProcessors.hashCode(); + result = 31 * result + myProcessorOptions.hashCode(); + result = 31 * result + (myGeneratedSourcesDirectoryName != null ? myGeneratedSourcesDirectoryName.hashCode() : 0); + result = 31 * result + myModuleNames.hashCode(); + return result; + } + + @Override + public String toString() { + return myName; + } +} + diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/ProcessAnnotationsAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/ProcessAnnotationsAction.java index 47757caee0c0..1b9c50e6ce2c 100644 --- a/java/compiler/impl/src/com/intellij/compiler/actions/ProcessAnnotationsAction.java +++ b/java/compiler/impl/src/com/intellij/compiler/actions/ProcessAnnotationsAction.java @@ -15,6 +15,7 @@ */ package com.intellij.compiler.actions; +import com.intellij.compiler.AnnotationProcessingConfiguration; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.impl.FileSetCompileScope; import com.intellij.compiler.impl.ModuleCompileScope; @@ -82,9 +83,12 @@ public class ProcessAnnotationsAction extends CompileActionBase { final Module module = LangDataKeys.MODULE.getData(dataContext); final Module moduleContext = LangDataKeys.MODULE_CONTEXT.getData(dataContext); - if (!compilerConfiguration.isAnnotationProcessorsEnabled() || - (!compilerConfiguration.isObtainProcessorsFromClasspath() && compilerConfiguration.getAnnotationProcessorsMap().isEmpty()) || - module != null && StringUtil.isEmpty(compilerConfiguration.getAnotationProcessedModules().get(module))) { + if (module == null) { + presentation.setEnabled(false); + return; + } + final AnnotationProcessingConfiguration profile = compilerConfiguration.getAnnotationProcessingConfiguration(module); + if (!profile.isEnabled() || (!profile.isObtainProcessorsFromClasspath() && profile.getProcessors().isEmpty())) { presentation.setEnabled(false); return; } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index f8dfcb2928c8..374d25888ca4 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -169,12 +169,10 @@ public class CompileDriver { final Pair outputs = new Pair(productionOutput, testOutput); myGenerationCompilerModuleToOutputDirMap.put(pair, outputs); } - if (config.isAnnotationProcessorsEnabled()) { - if (config.isAnnotationProcessingEnabled(module)) { - final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); - if (path != null) { - lookupVFile(lfs, path); // ensure the file is created and added to VFS - } + if (config.getAnnotationProcessingConfiguration(module).isEnabled()) { + final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); + if (path != null) { + lookupVFile(lfs, path); // ensure the file is created and added to VFS } } } @@ -395,7 +393,7 @@ public class CompileDriver { final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); final Set affected = new HashSet(Arrays.asList(context.getCompileScope().getAffectedModules())); for (Module module : affected) { - if (!config.isAnnotationProcessingEnabled(module)) { + if (!config.getAnnotationProcessingConfiguration(module).isEnabled()) { continue; } final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); @@ -1725,7 +1723,7 @@ public class CompileDriver { final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); if (context.isAnnotationProcessorsEnabled()) { for (Module module : modules) { - if (config.isAnnotationProcessingEnabled(module)) { + if (config.getAnnotationProcessingConfiguration(module).isEnabled()) { final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); if (path != null) { outputDirs.add(new File(path)); @@ -2366,7 +2364,7 @@ public class CompileDriver { modulesWithoutOutputPathSpecified.add(module.getName()); } } - if (config.isAnnotationProcessorsEnabled() && config.isAnnotationProcessingEnabled(module)) { + if (config.getAnnotationProcessingConfiguration(module).isEnabled()) { final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); if (path == null) { final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(module.getProject()); @@ -2458,9 +2456,9 @@ public class CompileDriver { if (chunkModules.size() <= 1) { continue; // no need to check one-module chunks } - if (config.isAnnotationProcessorsEnabled()) { + if (!useOutOfProcessBuild()) { for (Module chunkModule : chunkModules) { - if (config.isAnnotationProcessingEnabled(chunkModule)) { + if (config.getAnnotationProcessingConfiguration(chunkModule).isEnabled()) { showCyclesNotSupportedForAnnotationProcessors(chunkModules.toArray(new Module[chunkModules.size()])); return false; } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java index 36cf64ae998c..c7269a47eec1 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java @@ -120,7 +120,7 @@ public class AnnotationProcessingCompiler implements TranslatingCompiler{ } final Module module = context.getModuleByFile(file); if (module != null) { - if (!myConfig.isAnnotationProcessingEnabled(module)) { + if (!myConfig.getAnnotationProcessingConfiguration(module).isEnabled()) { return true; } final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java index c9675a861c96..1a2a44ef74e7 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java @@ -15,10 +15,7 @@ */ package com.intellij.compiler.impl.javaCompiler.javac; -import com.intellij.compiler.CompilerConfiguration; -import com.intellij.compiler.CompilerConfigurationImpl; -import com.intellij.compiler.CompilerIOUtil; -import com.intellij.compiler.OutputParser; +import com.intellij.compiler.*; import com.intellij.compiler.impl.CompilerUtil; import com.intellij.compiler.impl.javaCompiler.ExternalCompiler; import com.intellij.compiler.impl.javaCompiler.ModuleChunk; @@ -283,33 +280,21 @@ public class JavacCompiler extends ExternalCompiler { annotationProcessorsEnabled = false; } if (isAnnotationProcessing) { - final CompilerConfiguration config = CompilerConfiguration.getInstance(chunk.getProject()); + final AnnotationProcessingConfiguration config = CompilerConfiguration.getInstance(chunk.getProject()).getAnnotationProcessingConfiguration(chunk.getModules()[0]); additionalOptions.add("-Xprefer:source"); additionalOptions.add("-implicit:none"); additionalOptions.add("-proc:only"); if (!config.isObtainProcessorsFromClasspath()) { final String processorPath = config.getProcessorPath(); - if (processorPath.length() > 0) { - additionalOptions.add("-processorpath"); - additionalOptions.add(FileUtil.toSystemDependentName(processorPath)); - } + additionalOptions.add("-processorpath"); + additionalOptions.add(FileUtil.toSystemDependentName(processorPath)); } - for (Map.Entry entry : config.getAnnotationProcessorsMap().entrySet()) { + for (String processorName : config.getProcessors()) { additionalOptions.add("-processor"); - additionalOptions.add(entry.getKey()); - final String options = entry.getValue(); - if (options.length() > 0) { - StringTokenizer optionsTokenizer = new StringTokenizer(options, " ", false); - while (optionsTokenizer.hasMoreTokens()) { - final String token = optionsTokenizer.nextToken(); - if (token.startsWith("-A")) { - additionalOptions.add(token.substring("-A".length())); - } - else { - additionalOptions.add("-A" + token); - } - } - } + additionalOptions.add(processorName); + } + for (Map.Entry entry : config.getProcessorOptions().entrySet()) { + additionalOptions.add("-A" + entry.getKey() + "=" +entry.getValue()); } } else { diff --git a/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsConfigurable.java index d686fe2d8e18..dfb452cbd5d5 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsConfigurable.java +++ b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsConfigurable.java @@ -17,41 +17,17 @@ package com.intellij.compiler.options; import com.intellij.compiler.CompileServerManager; import com.intellij.compiler.CompilerConfiguration; +import com.intellij.compiler.CompilerConfigurationImpl; +import com.intellij.compiler.ProcessorConfigProfile; import com.intellij.compiler.server.BuildManager; -import com.intellij.openapi.fileChooser.FileChooser; -import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.*; -import com.intellij.ui.table.JBTable; -import com.intellij.util.containers.HashMap; -import com.intellij.util.ui.EditableModel; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.JTableHeader; -import javax.swing.table.TableCellEditor; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; +import java.util.List; import java.util.Map; /** @@ -59,15 +35,9 @@ import java.util.Map; * Date: Oct 5, 2009 */ public class AnnotationProcessorsConfigurable implements SearchableConfigurable, Configurable.NoScroll { - private ProcessedModulesTable myModulesTable; + private final Project myProject; - private JRadioButton myRbClasspath; - private JRadioButton myRbProcessorsPath; - private TextFieldWithBrowseButton myProcessorPathField; - private ProcessorTableModel myProcessorsModel; - private JCheckBox myCbEnableProcessing; - private JBTable myProcessorTable; - private JPanel myProcessorPanel; + private AnnotationProcessorsPanel myMainPanel; public AnnotationProcessorsConfigurable(final Project project) { myProject = project; @@ -95,163 +65,39 @@ public class AnnotationProcessorsConfigurable implements SearchableConfigurable, } public JComponent createComponent() { - final JPanel mainPanel = new JPanel(new GridBagLayout()); - - myCbEnableProcessing = new JCheckBox("Enable annotation processing"); - - myRbClasspath = new JRadioButton("Obtain processors from project classpath"); - myRbProcessorsPath = new JRadioButton("Processor path:"); - ButtonGroup group = new ButtonGroup(); - group.add(myRbClasspath); - group.add(myRbProcessorsPath); - - myProcessorPathField = new TextFieldWithBrowseButton(new ActionListener() { - public void actionPerformed(ActionEvent e) { - final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createAllButJarContentsDescriptor(); - final VirtualFile[] files = FileChooser.chooseFiles(descriptor, myProcessorPathField, myProject, null); - if (files.length > 0) { - final StringBuilder builder = new StringBuilder(); - for (VirtualFile file : files) { - if (builder.length() > 0) { - builder.append(File.pathSeparator); - } - builder.append(FileUtil.toSystemDependentName(file.getPath())); - } - myProcessorPathField.setText(builder.toString()); - } - } - }); - - final JPanel processorTablePanel = new JPanel(new BorderLayout()); - myProcessorsModel = new ProcessorTableModel(); - processorTablePanel.setBorder(IdeBorderFactory.createTitledBorder("Annotation Processors", false)); - myProcessorTable = new JBTable(myProcessorsModel); - myProcessorTable.getEmptyText().setText("No processors configured"); - myProcessorPanel = ToolbarDecorator.createDecorator(myProcessorTable) - .disableUpAction() - .disableDownAction() - .setAddAction(new AnActionButtonRunnable() { - @Override - public void run(AnActionButton anActionButton) { - final TableCellEditor cellEditor = myProcessorTable.getCellEditor(); - if (cellEditor != null) { - cellEditor.stopCellEditing(); - } - final ProcessorTableModel model = (ProcessorTableModel)myProcessorTable.getModel(); - model.addRow(); - TableUtil.editCellAt(myProcessorTable, model.getRowCount() - 1, ProcessorTableRow.NAME_COLUMN); - } - }) - .createPanel(); - - - - processorTablePanel.add(myProcessorPanel, BorderLayout.CENTER); - - myModulesTable = new ProcessedModulesTable(myProject); - myModulesTable.setBorder(IdeBorderFactory.createTitledBorder("Processed Modules", false)); - final JLabel noteMessage = new JLabel("Source files generated by annotation processors will be stored under the project output directory. " + - "To override this behaviour for certain modules you may specify the directory name in the table below. " + - "If specified, the directory will be created under corresponding module's content root."); - - final JLabel warning = new JLabel("WARNING!
" + - "All source files located in the generated sources output directory WILL BE EXCLUDED from annotation processing. " + - "If option 'Clear output directory on rebuild' is enabled, " + - "the entire contents of directories specified in the table below WILL BE CLEARED on rebuild."); - warning.setFont(warning.getFont().deriveFont(Font.BOLD)); - - mainPanel.add(myCbEnableProcessing, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - mainPanel.add(myRbClasspath, new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 0, 0, 0), 0, 0)); - mainPanel.add(myRbProcessorsPath, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0)); - mainPanel.add(myProcessorPathField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 0), 0, 0)); - mainPanel.add(processorTablePanel, new GridBagConstraints(0, 3, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 0, 0, 0), 0, 0)); - mainPanel.add(noteMessage, new GridBagConstraints(0, 4, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0)); - mainPanel.add(warning, new GridBagConstraints(0, 5, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0)); - mainPanel.add(myModulesTable, new GridBagConstraints(0, 6, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 0, 0, 0), 0, 0)); - //mainPanel.add(new AnnotationProcessorsPanel(myProject), new GridBagConstraints(0, 7, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 0, 0, 0), 0, 0)); - - - myRbClasspath.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - updateEnabledState(); - } - }); - - myProcessorTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { - if (!e.getValueIsAdjusting()) { - updateEnabledState(); - } - } - }); - - myCbEnableProcessing.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - updateEnabledState(); - } - }); - - updateEnabledState(); - - return mainPanel; - } - - private void updateEnabledState() { - final boolean enabled = myCbEnableProcessing.isSelected(); - final boolean useProcessorpath = !myRbClasspath.isSelected(); - myRbClasspath.setEnabled(enabled); - myRbProcessorsPath.setEnabled(enabled); - myProcessorPathField.setEnabled(enabled && useProcessorpath); - final AnActionButton addButton = ToolbarDecorator.findAddButton(myProcessorPanel); - if (addButton != null) { - addButton.setEnabled(enabled); - } - final AnActionButton removeButton = ToolbarDecorator.findRemoveButton(myProcessorPanel); - if (removeButton != null) { - removeButton.setEnabled(enabled && myProcessorTable.getSelectedRow() >= 0); - } - myProcessorTable.setEnabled(enabled); - final JTableHeader header = myProcessorTable.getTableHeader(); - if (header != null) { - header.repaint(); - } - myModulesTable.getComponent().setEnabled(enabled); + myMainPanel = new AnnotationProcessorsPanel(myProject); + return myMainPanel; } public boolean isModified() { - final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); - if (config.isAnnotationProcessorsEnabled() != myCbEnableProcessing.isSelected()) { - return true; - } - if (config.isObtainProcessorsFromClasspath() != myRbClasspath.isSelected()) { - return true; - } - if (!FileUtil.pathsEqual(config.getProcessorPath(), FileUtil.toSystemIndependentName(myProcessorPathField.getText().trim()))) { + final CompilerConfigurationImpl config = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject); + + if (!config.getDefaultProcessorProfile().equals(myMainPanel.getDefaultProfile())) { return true; } - final Map map = myProcessorsModel.exportToMap(); - if (!map.equals(config.getAnnotationProcessorsMap())) { + final Map configProfiles = new java.util.HashMap(); + for (ProcessorConfigProfile profile : config.getModuleProcessorProfiles()) { + configProfiles.put(profile.getName(), profile); + } + final List panelProfiles = myMainPanel.getModuleProfiles(); + if (configProfiles.size() != panelProfiles.size()) { return true; } - - if (!getMarkedModules().equals(config.getAnotationProcessedModules())) { - return true; + for (ProcessorConfigProfile panelProfile : panelProfiles) { + final ProcessorConfigProfile configProfile = configProfiles.get(panelProfile.getName()); + if (configProfile == null || !configProfile.equals(panelProfile)) { + return true; + } } return false; } public void apply() throws ConfigurationException { - final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); - config.setAnnotationProcessorsEnabled(myCbEnableProcessing.isSelected()); - - config.setObtainProcessorsFromClasspath(myRbClasspath.isSelected()); - config.setProcessorsPath(FileUtil.toSystemIndependentName(myProcessorPathField.getText().trim())); - - config.setAnnotationProcessorsMap(myProcessorsModel.exportToMap()); - - config.setAnotationProcessedModules(getMarkedModules()); + final CompilerConfigurationImpl config = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject); + config.setDefaultProcessorProfile(myMainPanel.getDefaultProfile()); + config.setModuleProcessorProfiles(myMainPanel.getModuleProfiles()); SwingUtilities.invokeLater(new Runnable() { public void run() { CompileServerManager.getInstance().sendReloadRequest(myProject); @@ -260,161 +106,12 @@ public class AnnotationProcessorsConfigurable implements SearchableConfigurable, }); } - private Map getMarkedModules() { - final Map result = new HashMap(); - for (Pair pair : myModulesTable.getAllModules()) { - result.put(pair.getFirst(), pair.getSecond()); - } - return result; - } - public void reset() { - final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); - myCbEnableProcessing.setSelected(config.isAnnotationProcessorsEnabled()); - - final boolean obtainFromClasspath = config.isObtainProcessorsFromClasspath(); - if (obtainFromClasspath) { - myRbClasspath.setSelected(true); - } - else { - myRbProcessorsPath.setSelected(true); - } - - myProcessorPathField.setText(FileUtil.toSystemDependentName(config.getProcessorPath())); - - myProcessorsModel.setProcessorMap(config.getAnnotationProcessorsMap()); - - myModulesTable.removeAllElements(); - for (final Module module : ModuleManager.getInstance(myProject).getModules()) { - if (config.isAnnotationProcessingEnabled(module)) { - myModulesTable.addModule(module, config.getGeneratedSourceDirName(module)); - } - } - myModulesTable.sort(new Comparator() { - public int compare(Module o1, Module o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + final CompilerConfigurationImpl config = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject); + myMainPanel.initProfiles(config.getDefaultProcessorProfile(), config.getModuleProcessorProfiles()); } public void disposeUIResources() { } - private static class ProcessorTableModel extends AbstractTableModel implements EditableModel { - private final java.util.List myRows = new ArrayList(); - - public String getColumnName(int column) { - switch (column) { - case ProcessorTableRow.NAME_COLUMN: return "Processor FQ Name"; - case ProcessorTableRow.OPTIONS_COLUMN : return "Processor Run Options (space-separated \"key=value\" pairs)"; - } - return super.getColumnName(column); - } - - public Class getColumnClass(int columnIndex) { - return String.class; - } - - public int getRowCount() { - return myRows.size(); - } - - public int getColumnCount() { - return 2; - } - - public boolean isCellEditable(int rowIndex, int columnIndex) { - return columnIndex == ProcessorTableRow.NAME_COLUMN || columnIndex == ProcessorTableRow.OPTIONS_COLUMN; - } - - public Object getValueAt(int rowIndex, int columnIndex) { - final ProcessorTableRow row = myRows.get(rowIndex); - switch (columnIndex) { - case ProcessorTableRow.NAME_COLUMN: return row.name; - case ProcessorTableRow.OPTIONS_COLUMN : return row.options; - } - return null; - } - - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - if (aValue != null) { - final ProcessorTableRow row = myRows.get(rowIndex); - switch (columnIndex) { - case ProcessorTableRow.NAME_COLUMN: - row.name = (String)aValue; - break; - case ProcessorTableRow.OPTIONS_COLUMN: - row.options = (String)aValue; - break; - } - } - } - - public void removeRow(int idx) { - myRows.remove(idx); - fireTableRowsDeleted(idx, idx); - } - - @Override - public void exchangeRows(int oldIndex, int newIndex) { - } - - public void addRow() { - myRows.add(new ProcessorTableRow()); - final int index = myRows.size() - 1; - fireTableRowsInserted(index, index); - } - - public void setProcessorMap(Map processorMap) { - clear(); - if (processorMap.size() > 0) { - for (Map.Entry entry : processorMap.entrySet()) { - myRows.add(new ProcessorTableRow(entry.getKey(), entry.getValue())); - } - Collections.sort(myRows, new Comparator() { - public int compare(ProcessorTableRow o1, ProcessorTableRow o2) { - return o1.name.compareToIgnoreCase(o2.name); - } - }); - fireTableRowsInserted(0, processorMap.size()-1); - } - } - - public void clear() { - final int count = myRows.size(); - if (count > 0) { - myRows.clear(); - fireTableRowsDeleted(0, count-1); - } - } - - public Map exportToMap() { - final Map map = new HashMap(); - for (ProcessorTableRow row : myRows) { - if (row.name != null) { - final String name = row.name.trim(); - if (name.length() > 0 && !map.containsKey(name)) { - map.put(name, row.options); - } - } - } - return map; - } - } - - private static final class ProcessorTableRow { - public static final int NAME_COLUMN = 0; - public static final int OPTIONS_COLUMN = 1; - - public String name = ""; - public String options = ""; - - public ProcessorTableRow() { - } - - public ProcessorTableRow(String name, String options) { - this.name = name != null? name : ""; - this.options = options != null? options : ""; - } - } } diff --git a/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsPanel.java b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsPanel.java index f8d4ad8ec68d..dc5f2329b41d 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsPanel.java +++ b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsPanel.java @@ -15,6 +15,7 @@ */ package com.intellij.compiler.options; +import com.intellij.compiler.ProcessorConfigProfile; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ShortcutSet; @@ -25,10 +26,12 @@ import com.intellij.openapi.ui.InputValidatorEx; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.AnActionButton; import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.ToolbarDecorator; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.components.JBList; @@ -52,16 +55,20 @@ import java.util.List; */ @SuppressWarnings({"unchecked", "UseOfObsoleteCollectionType"}) public class AnnotationProcessorsPanel extends JPanel { - private final Map> profiles = new HashMap>(); - private static final String DEFAULT_PROFILE = "Default"; + private final ProcessorConfigProfile myDefaultProfile = new ProcessorConfigProfile(""); + private final List myModuleProfiles = new ArrayList(); + private final Map myAllModulesMap = new HashMap(); private final Project myProject; private final Tree myTree; - private JPanel myContentPanel; + private final ProcessorProfilePanel myProfilePanel; + private ProcessorConfigProfile mySelectedProfile = null; public AnnotationProcessorsPanel(Project project) { super(new BorderLayout()); myProject = project; - loadProfiles(); + for (Module module : ModuleManager.getInstance(project).getModules()) { + myAllModulesMap.put(module.getName(), module); + } myTree = new Tree(new MyTreeModel()); myTree.setRootVisible(false); final JPanel treePanel = @@ -70,37 +77,39 @@ public class AnnotationProcessorsPanel extends JPanel { public void actionPerformed(AnActionEvent e) { final MyModuleNode node = (MyModuleNode)myTree.getSelectionPath().getLastPathComponent(); final TreePath[] selectedNodes = myTree.getSelectionPaths(); - final String key = ((MyProfileNode)node.getParent()).myKey; - final List profileNames = new ArrayList(); - profileNames.add(DEFAULT_PROFILE); - profileNames.addAll(profiles.keySet()); - profileNames.remove(key); - final JBList list = new JBList(profileNames); + final ProcessorConfigProfile nodeProfile = ((ProfileNode)node.getParent()).myProfile; + final List profiles = new ArrayList(); + profiles.add(myDefaultProfile); + for (ProcessorConfigProfile profile : myModuleProfiles) { + profiles.add(profile); + } + profiles.remove(nodeProfile); + final JBList list = new JBList(profiles); final JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list) .setTitle("Move to") .setItemChoosenCallback(new Runnable() { @Override public void run() { final Object value = list.getSelectedValue(); - if (value instanceof String) { + if (value instanceof ProcessorConfigProfile) { + final ProcessorConfigProfile chosenProfile = (ProcessorConfigProfile)value; final Module toSelect = (Module)node.getUserObject(); if (selectedNodes != null) { for (TreePath selectedNode : selectedNodes) { - final Object n = selectedNode.getLastPathComponent(); - - if (n instanceof MyModuleNode) { - Module module = (Module)((MyModuleNode)n).getUserObject(); - if (!DEFAULT_PROFILE.equals(key)) { - profiles.get(key).remove(module); + final Object node = selectedNode.getLastPathComponent(); + if (node instanceof MyModuleNode) { + final Module module = (Module)((MyModuleNode)node).getUserObject(); + if (nodeProfile != myDefaultProfile) { + nodeProfile.removeModuleName(module.getName()); } - if (!DEFAULT_PROFILE.equals(value)) { - profiles.get(value).add(module); + if (chosenProfile != myDefaultProfile) { + chosenProfile.addModuleName(module.getName()); } } } } - final MyRootNode root = (MyRootNode)myTree.getModel().getRoot(); + final RootNode root = (RootNode)myTree.getModel().getRoot(); root.sync(); final DefaultMutableTreeNode node = TreeUtil.findNodeWithObject(root, toSelect); if (node != null) { @@ -123,15 +132,13 @@ public class AnnotationProcessorsPanel extends JPanel { public boolean isEnabled() { return myTree.getSelectionPath() != null && myTree.getSelectionPath().getLastPathComponent() instanceof MyModuleNode - && !profiles.isEmpty(); + && !myModuleProfiles.isEmpty(); } }).createPanel(); add(treePanel, BorderLayout.WEST); myTree.setCellRenderer(new MyCellRenderer()); - ((MyRootNode)myTree.getModel().getRoot()).sync(); - myContentPanel = new JPanel(new BorderLayout()); + myTree.addTreeSelectionListener(new TreeSelectionListener() { - String currentProfile = null; @Override public void valueChanged(TreeSelectionEvent e) { final TreePath path = myTree.getSelectionPath(); @@ -140,25 +147,56 @@ public class AnnotationProcessorsPanel extends JPanel { if (node instanceof MyModuleNode) { node = ((MyModuleNode)node).getParent(); } - if (node instanceof MyProfileNode) { - if (!StringUtil.equals(currentProfile, ((MyProfileNode)node).myKey)) { - currentProfile = ((MyProfileNode)node).myKey; - myContentPanel.removeAll(); - myContentPanel.add(getComponentForProfile(currentProfile), BorderLayout.CENTER); - revalidate(); - repaint(); + if (node instanceof ProfileNode) { + final ProcessorConfigProfile nodeProfile = ((ProfileNode)node).myProfile; + final ProcessorConfigProfile selectedProfile = mySelectedProfile; + if (nodeProfile != selectedProfile) { + if (selectedProfile != null) { + myProfilePanel.saveTo(selectedProfile); + } + mySelectedProfile = nodeProfile; + myProfilePanel.setProfile(nodeProfile); } } } } }); - add(myContentPanel, BorderLayout.CENTER); + myProfilePanel = new ProcessorProfilePanel(project); + myProfilePanel.setBorder(IdeBorderFactory.createEmptyBorder(0, 6, 0, 0)); + add(myProfilePanel, BorderLayout.CENTER); } + public void initProfiles(ProcessorConfigProfile defaultProfile, Collection moduleProfiles) { + myDefaultProfile.initFrom(defaultProfile); + myModuleProfiles.clear(); + for (ProcessorConfigProfile profile : moduleProfiles) { + ProcessorConfigProfile copy = new ProcessorConfigProfile(""); + copy.initFrom(profile); + myModuleProfiles.add(copy); + } + final RootNode root = (RootNode)myTree.getModel().getRoot(); + root.sync(); + final DefaultMutableTreeNode node = TreeUtil.findNodeWithObject(root, myDefaultProfile); + if (node != null) { + TreeUtil.selectNode(myTree, node); + } - private JComponent getComponentForProfile(String profile) { - //TODO[jeka] correct panel - return new JLabel(profile, SwingConstants.CENTER); + } + + public ProcessorConfigProfile getDefaultProfile() { + final ProcessorConfigProfile selectedProfile = mySelectedProfile; + if (myDefaultProfile == selectedProfile) { + myProfilePanel.saveTo(selectedProfile); + } + return myDefaultProfile; + } + + public List getModuleProfiles() { + final ProcessorConfigProfile selectedProfile = mySelectedProfile; + if (myDefaultProfile != selectedProfile) { + myProfilePanel.saveTo(selectedProfile); + } + return myModuleProfiles; } private static void expand(JTree tree) { @@ -174,48 +212,74 @@ public class AnnotationProcessorsPanel extends JPanel { while (true); } - private void loadProfiles() { - //TODO[jeka] init profiles map - } - private class MyTreeModel extends DefaultTreeModel implements EditableTreeModel{ public MyTreeModel() { - super(new MyRootNode()); + super(new RootNode()); } @Override public TreePath addNode(TreePath parentOrNeighbour) { - final String profile = Messages.showInputDialog(myProject, "Profile name", "Create new profile", null, "", new InputValidatorEx() { - @Override - public boolean checkInput(String inputString) { - return !DEFAULT_PROFILE.equals(inputString) && !profiles.containsKey(inputString) && !StringUtil.isEmpty(inputString); - } + final String newProfileName = Messages.showInputDialog( + myProject, "Profile name", "Create new profile", null, "", + new InputValidatorEx() { + @Override + public boolean checkInput(String inputString) { + if (StringUtil.isEmpty(inputString) || + Comparing.equal(inputString, myDefaultProfile.getName())) { + return false; + } + for (ProcessorConfigProfile profile : myModuleProfiles) { + if (Comparing.equal(inputString, profile.getName())) { + return false; + } + } + return true; + } - @Override - public boolean canClose(String inputString) { - return checkInput(inputString); - } + @Override + public boolean canClose(String inputString) { + return checkInput(inputString); + } - @Override - public String getErrorText(String inputString) { - if (checkInput(inputString)) return null; - return StringUtil.isEmpty(inputString) ? "Profile name shouldn't be empty" - : "Profile " + inputString + " already exists"; + @Override + public String getErrorText(String inputString) { + if (checkInput(inputString)) { + return null; + } + return StringUtil.isEmpty(inputString) + ? "Profile name shouldn't be empty" + : "Profile " + inputString + " already exists"; + } + }); + if (newProfileName != null) { + final ProcessorConfigProfile profile = new ProcessorConfigProfile(newProfileName); + myModuleProfiles.add(profile); + ((DataSynchronizable)getRoot()).sync(); + final DefaultMutableTreeNode object = TreeUtil.findNodeWithObject((DefaultMutableTreeNode)getRoot(), profile); + if (object != null) { + TreeUtil.selectNode(myTree, object); } - }); - if (profile != null) { - profiles.put(profile, new ArrayList()); - } - ((SyncWithMap)getRoot()).sync(); - final DefaultMutableTreeNode object = TreeUtil.findNodeWithObject((DefaultMutableTreeNode)getRoot(), profile); - if (object != null) { - TreeUtil.selectNode(myTree, object); } return null; } @Override - public void removeNode(TreePath parent) { + public void removeNode(TreePath nodePath) { + Object node = nodePath.getLastPathComponent(); + if (node instanceof ProfileNode) { + final ProcessorConfigProfile nodeProfile = ((ProfileNode)node).myProfile; + if (nodeProfile != myDefaultProfile) { + if (mySelectedProfile == nodeProfile) { + mySelectedProfile = null; + } + myModuleProfiles.remove(nodeProfile); + ((DataSynchronizable)getRoot()).sync(); + final DefaultMutableTreeNode object = TreeUtil.findNodeWithObject((DefaultMutableTreeNode)getRoot(), myDefaultProfile); + if (object != null) { + TreeUtil.selectNode(myTree, object); + } + } + } } @Override @@ -225,14 +289,14 @@ public class AnnotationProcessorsPanel extends JPanel { } - private class MyRootNode extends DefaultMutableTreeNode implements SyncWithMap { + private class RootNode extends DefaultMutableTreeNode implements DataSynchronizable { @Override - public SyncWithMap sync() { + public DataSynchronizable sync() { final Vector newKids = new Vector(); - for (String key : profiles.keySet()) { - newKids.add(new MyProfileNode(key, this).sync()); + newKids.add(new ProfileNode(myDefaultProfile, this, true).sync()); + for (ProcessorConfigProfile profile : myModuleProfiles) { + newKids.add(new ProfileNode(profile, this, false).sync()); } - newKids.add(new MyProfileNode(DEFAULT_PROFILE, this).sync()); children = newKids; ((DefaultTreeModel)myTree.getModel()).reload(); expand(myTree); @@ -240,33 +304,44 @@ public class AnnotationProcessorsPanel extends JPanel { } } - private interface SyncWithMap { - SyncWithMap sync(); + private interface DataSynchronizable { + DataSynchronizable sync(); } - private class MyProfileNode extends DefaultMutableTreeNode implements SyncWithMap { - private final String myKey; + private class ProfileNode extends DefaultMutableTreeNode implements DataSynchronizable { + private final ProcessorConfigProfile myProfile; + private final boolean myIsDefault; - public MyProfileNode(String key, MyRootNode parent) { - super(key); + public ProfileNode(ProcessorConfigProfile profile, RootNode parent, boolean isDefault) { + super(profile); setParent(parent); - myKey = key; + myIsDefault = isDefault; + myProfile = profile; } @Override - public SyncWithMap sync() { - final List nodeModules; - if (DEFAULT_PROFILE.equals(myKey)) { - final Module[] allModules = ModuleManager.getInstance(myProject).getSortedModules(); - nodeModules = new ArrayList(Arrays.asList(allModules)); - for (List modules : profiles.values()) { - for (Module module : modules) { - nodeModules.remove(module); + public DataSynchronizable sync() { + final List nodeModules = new ArrayList(); + if (myIsDefault) { + final Set nonDefaultProfileModules = new HashSet(); + for (ProcessorConfigProfile profile : myModuleProfiles) { + nonDefaultProfileModules.addAll(profile.getModuleNames()); + } + for (Map.Entry entry : myAllModulesMap.entrySet()) { + if (!nonDefaultProfileModules.contains(entry.getKey())) { + nodeModules.add(entry.getValue()); } } - } else { - nodeModules = profiles.get(myKey); } + else { + for (String moduleName : myProfile.getModuleNames()) { + final Module module = myAllModulesMap.get(moduleName); + if (module != null) { + nodeModules.add(module); + } + } + } + Collections.sort(nodeModules, ModuleComparator.INSTANCE); final Vector vector = new Vector(); for (Module module : nodeModules) { vector.add(new MyModuleNode(module, this)); @@ -274,32 +349,39 @@ public class AnnotationProcessorsPanel extends JPanel { children = vector; return this; } + } - private class MyModuleNode extends DefaultMutableTreeNode { - public MyModuleNode(Module module, MyProfileNode parent) { + private static class MyModuleNode extends DefaultMutableTreeNode { + public MyModuleNode(Module module, ProfileNode parent) { super(module); setParent(parent); setAllowsChildren(false); } } - private class MyCellRenderer extends ColoredTreeCellRenderer { + private static class MyCellRenderer extends ColoredTreeCellRenderer { + private static final Icon MODULE_ICON = IconLoader.getIcon("/nodes/ModuleClosed.png"); + @Override - public void customizeCellRenderer(JTree tree, - Object value, - boolean selected, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) { - if (value instanceof MyProfileNode) { - append(((MyProfileNode)value).myKey); - } else if (value instanceof MyModuleNode) { + public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + if (value instanceof ProfileNode) { + append(((ProfileNode)value).myProfile.getName()); + } + else if (value instanceof MyModuleNode) { final Module module = (Module)((MyModuleNode)value).getUserObject(); - setIcon(IconLoader.getIcon("/nodes/ModuleClosed.png")); + setIcon(MODULE_ICON); append(module.getName()); } } } + + private static class ModuleComparator implements Comparator { + static final ModuleComparator INSTANCE = new ModuleComparator(); + @Override + public int compare(Module o1, Module o2) { + return o1.getName().compareTo(o2.getName()); + } + } + } diff --git a/java/compiler/impl/src/com/intellij/compiler/options/ProcessorProfilePanel.java b/java/compiler/impl/src/com/intellij/compiler/options/ProcessorProfilePanel.java new file mode 100644 index 000000000000..867317d8bbc3 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/options/ProcessorProfilePanel.java @@ -0,0 +1,454 @@ +/* + * Copyright 2000-2012 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.compiler.ProcessorConfigProfile; +import com.intellij.openapi.fileChooser.FileChooser; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.*; +import com.intellij.ui.table.JBTable; +import com.intellij.util.ui.EditableModel; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableModel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.io.File; +import java.util.*; +import java.util.List; + +/** + * @author Eugene Zhuravlev + * Date: 5/28/12 + */ +public class ProcessorProfilePanel extends JPanel { + private final Project myProject; + + private JRadioButton myRbClasspath; + private JRadioButton myRbProcessorsPath; + private TextFieldWithBrowseButton myProcessorPathField; + private JTextField myGeneratedSourcesDirNameField; + private ProcessorTableModel myProcessorsModel; + private JCheckBox myCbEnableProcessing; + private JBTable myProcessorTable; + private JBTable myOptionsTable; + private JPanel myProcessorPanel; + private JPanel myOptionsPanel; + private OptionsTableModel myOptionsModel; + + + public ProcessorProfilePanel(Project project) { + super(new GridBagLayout()); + myProject = project; + + myCbEnableProcessing = new JCheckBox("Enable annotation processing"); + + myRbClasspath = new JRadioButton("Obtain processors from project classpath"); + myRbProcessorsPath = new JRadioButton("Processor path:"); + ButtonGroup group = new ButtonGroup(); + group.add(myRbClasspath); + group.add(myRbProcessorsPath); + + myProcessorPathField = new TextFieldWithBrowseButton(new ActionListener() { + public void actionPerformed(ActionEvent e) { + final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createAllButJarContentsDescriptor(); + final VirtualFile[] files = FileChooser.chooseFiles(descriptor, myProcessorPathField, myProject, null); + if (files.length > 0) { + final StringBuilder builder = new StringBuilder(); + for (VirtualFile file : files) { + if (builder.length() > 0) { + builder.append(File.pathSeparator); + } + builder.append(FileUtil.toSystemDependentName(file.getPath())); + } + myProcessorPathField.setText(builder.toString()); + } + } + }); + + final JPanel processorTablePanel = new JPanel(new BorderLayout()); + myProcessorsModel = new ProcessorTableModel(); + processorTablePanel.setBorder(IdeBorderFactory.createTitledBorder("Annotation Processors", false)); + myProcessorTable = new JBTable(myProcessorsModel); + myProcessorTable.getEmptyText().setText("Compiler will run all automatically discovered processors"); + myProcessorPanel = createTablePanel(myProcessorTable); + processorTablePanel.add(myProcessorPanel, BorderLayout.CENTER); + + final JPanel optionsTablePanel = new JPanel(new BorderLayout()); + myOptionsModel = new OptionsTableModel(); + optionsTablePanel.setBorder(IdeBorderFactory.createTitledBorder("Annotation Processor options", false)); + myOptionsTable = new JBTable(myOptionsModel); + myOptionsTable.getEmptyText().setText("No processor-specific options configured"); + myOptionsPanel = createTablePanel(myOptionsTable); + optionsTablePanel.add(myOptionsPanel, BorderLayout.CENTER); + + myGeneratedSourcesDirNameField = new JTextField(); + + + final JLabel warning = new JLabel("WARNING!
" + + /*"All source files located in the generated sources output directory WILL BE EXCLUDED from annotation processing. " +*/ + "If option 'Clear output directory on rebuild' is enabled, " + + "the entire contents of directories specified in the table below WILL BE CLEARED on rebuild."); + warning.setFont(warning.getFont().deriveFont(Font.BOLD)); + + add(myCbEnableProcessing, + new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + add(myRbClasspath, + new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 0, 0, 0), 0, 0)); + add(myRbProcessorsPath, + new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0)); + add(myProcessorPathField, + new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 0), 0, 0)); + + final JLabel noteMessage = new JLabel("Source files generated by annotation processors will be stored under the project output directory. " + + "To override this behaviour for this profile you may specify the directory name in the field below. " + + "If specified, the directory will be created under corresponding module's content root."); + add(noteMessage, + new GridBagConstraints(0, 3, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0)); + + add(new JLabel("Directory name:"), + new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0)); + add(myGeneratedSourcesDirNameField, + new GridBagConstraints(1, 4, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0)); + + add(processorTablePanel, + new GridBagConstraints(0, 5, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 0, 0, 0), 0, 0)); + add(optionsTablePanel, + new GridBagConstraints(0, 6, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 0, 0, 0), 0, 0)); + add(warning, + new GridBagConstraints(0, 7, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0)); + + myRbClasspath.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + updateEnabledState(); + } + }); + + myProcessorTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + if (!e.getValueIsAdjusting()) { + updateEnabledState(); + } + } + }); + + myCbEnableProcessing.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + updateEnabledState(); + } + }); + + updateEnabledState(); + + } + + public void setProfile(ProcessorConfigProfile config) { + myCbEnableProcessing.setSelected(config.isEnabled()); + + (config.isObtainProcessorsFromClasspath()? myRbClasspath : myRbProcessorsPath).setSelected(true); + myProcessorPathField.setText(FileUtil.toSystemDependentName(config.getProcessorPath())); + + final String srcDirName = config.getGeneratedSourcesDirectoryName(); + myGeneratedSourcesDirNameField.setText(srcDirName != null? srcDirName.trim() : ""); + myProcessorsModel.setProcessors(config.getProcessors()); + myOptionsModel.setOptions(config.getProcessorOptions()); + + updateEnabledState(); + } + + public void saveTo(ProcessorConfigProfile profile) { + profile.setEnabled(myCbEnableProcessing.isSelected()); + profile.setObtainProcessorsFromClasspath(myRbClasspath.isSelected()); + profile.setProcessorPath(myProcessorPathField.getText().trim()); + final String dirName = myGeneratedSourcesDirNameField.getText().trim(); + + profile.setGeneratedSourcesDirectoryName(StringUtil.isEmpty(dirName)? null : dirName); + + profile.clearProcessors(); + for (String processor : myProcessorsModel.getProcessors()) { + profile.addProcessor(processor); + } + profile.clearProcessorOptions(); + for (Map.Entry entry : myOptionsModel.getOptions().entrySet()) { + profile.setOption(entry.getKey(), entry.getValue()); + } + } + + private static JPanel createTablePanel(final JBTable table) { + return ToolbarDecorator.createDecorator(table) + .disableUpAction() + .disableDownAction() + .setAddAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton anActionButton) { + final TableCellEditor cellEditor = table.getCellEditor(); + if (cellEditor != null) { + cellEditor.stopCellEditing(); + } + final TableModel model = table.getModel(); + ((EditableModel)model).addRow(); + TableUtil.editCellAt(table, model.getRowCount() - 1, 0); + } + }) + .createPanel(); + } + + private void updateEnabledState() { + final boolean enabled = myCbEnableProcessing.isSelected(); + final boolean useProcessorpath = !myRbClasspath.isSelected(); + myRbClasspath.setEnabled(enabled); + myRbProcessorsPath.setEnabled(enabled); + myProcessorPathField.setEnabled(enabled && useProcessorpath); + updateTable(myProcessorPanel, myProcessorTable, enabled); + updateTable(myOptionsPanel, myOptionsTable, enabled); + myGeneratedSourcesDirNameField.setEnabled(enabled); + } + + private static void updateTable(final JPanel tablePanel, final JBTable table, boolean enabled) { + final AnActionButton addButton = ToolbarDecorator.findAddButton(tablePanel); + if (addButton != null) { + addButton.setEnabled(enabled); + } + final AnActionButton removeButton = ToolbarDecorator.findRemoveButton(tablePanel); + if (removeButton != null) { + removeButton.setEnabled(enabled && table.getSelectedRow() >= 0); + } + table.setEnabled(enabled); + final JTableHeader header = table.getTableHeader(); + if (header != null) { + header.repaint(); + } + } + + private static class OptionsTableModel extends AbstractTableModel implements EditableModel { + private final java.util.List myRows = new ArrayList(); + + public String getColumnName(int column) { + switch (column) { + case 0: return "Option Name"; + case 1: return "Value"; + } + return super.getColumnName(column); + } + + public Class getColumnClass(int columnIndex) { + return String.class; + } + + public int getRowCount() { + return myRows.size(); + } + + public int getColumnCount() { + return 2; + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == 0 || columnIndex == 1; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + switch (columnIndex) { + case 0: return myRows.get(rowIndex).key; + case 1: return myRows.get(rowIndex).value; + } + return null; + } + + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + if (aValue != null) { + switch (columnIndex) { + case 0: + myRows.get(rowIndex).key = (String)aValue; + break; + case 1: + myRows.get(rowIndex).value = (String)aValue; + break; + } + } + } + + public void removeRow(int idx) { + myRows.remove(idx); + fireTableRowsDeleted(idx, idx); + } + + @Override + public void exchangeRows(int oldIndex, int newIndex) { + } + + public void addRow() { + myRows.add(new KeyValuePair()); + final int index = myRows.size() - 1; + fireTableRowsInserted(index, index); + } + + public void setOptions(Map options) { + clear(); + if (!options.isEmpty()) { + for (Map.Entry entry : options.entrySet()) { + myRows.add(new KeyValuePair(entry.getKey(), entry.getValue())); + } + Collections.sort(myRows, new Comparator() { + @Override + public int compare(KeyValuePair o1, KeyValuePair o2) { + return o1.key.compareToIgnoreCase(o2.key); + } + }); + fireTableRowsInserted(0, options.size()-1); + } + } + + public void clear() { + final int count = myRows.size(); + if (count > 0) { + myRows.clear(); + fireTableRowsDeleted(0, count-1); + } + } + + public Map getOptions() { + final Map map = new java.util.HashMap(); + for (KeyValuePair pair : myRows) { + map.put(pair.key.trim(), pair.value.trim()); + } + map.remove(""); + return map; + } + + private static final class KeyValuePair { + String key; + String value; + + KeyValuePair() { + this("", ""); + } + + KeyValuePair(String key, String value) { + this.key = key; + this.value = value; + } + } + } + + private static class ProcessorTableModel extends AbstractTableModel implements EditableModel { + private final List myRows = new ArrayList(); + + public String getColumnName(int column) { + switch (column) { + case 0: return "Processor FQ Name"; + } + return super.getColumnName(column); + } + + public Class getColumnClass(int columnIndex) { + return String.class; + } + + public int getRowCount() { + return myRows.size(); + } + + public int getColumnCount() { + return 1; + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == 0; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + switch (columnIndex) { + case 0: return myRows.get(rowIndex); + } + return null; + } + + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + if (aValue != null) { + switch (columnIndex) { + case 0: + myRows.set(rowIndex, (String)aValue); + break; + } + } + } + + public void removeRow(int idx) { + myRows.remove(idx); + fireTableRowsDeleted(idx, idx); + } + + @Override + public void exchangeRows(int oldIndex, int newIndex) { + } + + public void addRow() { + myRows.add(""); + final int index = myRows.size() - 1; + fireTableRowsInserted(index, index); + } + + public void setProcessors(Collection processors) { + clear(); + if (!processors.isEmpty()) { + for (String processor : processors) { + myRows.add(processor); + } + Collections.sort(myRows, new Comparator() { + public int compare(String o1, String o2) { + return o1.compareToIgnoreCase(o2); + } + }); + fireTableRowsInserted(0, processors.size()-1); + } + } + + public void clear() { + final int count = myRows.size(); + if (count > 0) { + myRows.clear(); + fireTableRowsDeleted(0, count-1); + } + } + + public Collection getProcessors() { + final Set set = new HashSet(); + for (String row : myRows) { + if (row != null) { + set.add(row.trim()); + } + } + set.remove(""); + return set; + } + } + +} diff --git a/java/compiler/openapi/src/com/intellij/compiler/AnnotationProcessingConfiguration.java b/java/compiler/openapi/src/com/intellij/compiler/AnnotationProcessingConfiguration.java new file mode 100644 index 000000000000..bd5a9b0cf4de --- /dev/null +++ b/java/compiler/openapi/src/com/intellij/compiler/AnnotationProcessingConfiguration.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2012 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; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + +/** + * @author Eugene Zhuravlev + * Date: 5/27/12 + */ +public interface AnnotationProcessingConfiguration { + boolean isEnabled(); + + @NotNull + String getProcessorPath(); + + @Nullable + String getGeneratedSourcesDirectoryName(); + + @NotNull + Set getProcessors(); + + @NotNull + Map getProcessorOptions(); + + boolean isObtainProcessorsFromClasspath(); +} diff --git a/java/compiler/openapi/src/com/intellij/compiler/CompilerConfiguration.java b/java/compiler/openapi/src/com/intellij/compiler/CompilerConfiguration.java index 12e5d3354995..7bbba891ff29 100644 --- a/java/compiler/openapi/src/com/intellij/compiler/CompilerConfiguration.java +++ b/java/compiler/openapi/src/com/intellij/compiler/CompilerConfiguration.java @@ -19,10 +19,9 @@ 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 org.jetbrains.annotations.Nullable; -import java.util.Map; - public abstract class CompilerConfiguration { // need this flag for profiling purposes. In production code is always set to 'true' public static final boolean MAKE_ENABLED = true; @@ -35,6 +34,14 @@ public abstract class CompilerConfiguration { public abstract void setBytecodeTargetLevel(Module module, String level); + @NotNull + public abstract AnnotationProcessingConfiguration getAnnotationProcessingConfiguration(Module module); + + /** + * @return true if exists at least one enabled annotation processing profile + */ + public abstract boolean isAnnotationProcessorsEnabled(); + public static CompilerConfiguration getInstance(Project project) { return project.getComponent(CompilerConfiguration.class); } @@ -50,28 +57,4 @@ public abstract class CompilerConfiguration { public abstract boolean isAddNotNullAssertions(); public abstract void setAddNotNullAssertions(boolean enabled); - - public abstract boolean isAnnotationProcessorsEnabled(); - - public abstract void setAnnotationProcessorsEnabled(boolean enableAnnotationProcessors); - - public abstract boolean isObtainProcessorsFromClasspath(); - - public abstract void setObtainProcessorsFromClasspath(boolean obtainProcessorsFromClasspath); - - public abstract String getProcessorPath(); - - public abstract void setProcessorsPath(String processorsPath); - - public abstract Map getAnnotationProcessorsMap(); - - public abstract void setAnnotationProcessorsMap(Map map); - - public abstract void setAnotationProcessedModules(Map modules); - - public abstract Map getAnotationProcessedModules(); - - public abstract boolean isAnnotationProcessingEnabled(Module module); - - public abstract String getGeneratedSourceDirName(Module module); } \ No newline at end of file diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java index 8f0b647b6871..b9764bdba829 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java @@ -216,8 +216,8 @@ public class CompilerPaths { public static String getAnnotationProcessorsGenerationPath(Module module) { final CompilerConfiguration config = CompilerConfiguration.getInstance(module.getProject()); - final String sourceDirName = config.getGeneratedSourceDirName(module); - if (sourceDirName != null && sourceDirName.length() > 0) { + final String sourceDirName = config.getAnnotationProcessingConfiguration(module).getGeneratedSourcesDirectoryName(); + if (!StringUtil.isEmpty(sourceDirName)) { final String[] roots = ModuleRootManager.getInstance(module).getContentRootUrls(); if (roots.length == 0) { return null; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java index d5140ce06b0c..49161f672335 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java @@ -46,6 +46,7 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler private final CanceledStatus myCancelStatus; private float myDone = -1.0f; private EventDispatcher myListeners = EventDispatcher.create(BuildListener.class); + private Map myAnnotationProcessingProfileMap; public CompileContext(CompileScope scope, ProjectDescriptor pd, boolean isMake, @@ -103,6 +104,32 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler myListeners.removeListener(listener); } + @NotNull + public AnnotationProcessingProfile getAnnotationProcessingProfile(Module module) { + final CompilerConfiguration compilerConfig = getProject().getCompilerConfiguration(); + Map map = myAnnotationProcessingProfileMap; + if (map == null) { + map = new HashMap(); + final Map namesMap = new HashMap(); + for (Module m : getProject().getModules().values()) { + namesMap.put(m.getName(), m); + } + if (!namesMap.isEmpty()) { + for (AnnotationProcessingProfile profile : compilerConfig.getModuleAnnotationProcessingProfiles()) { + for (String name : profile.getProcessModule()) { + final Module mod = namesMap.get(name); + if (mod != null) { + map.put(mod, profile); + } + } + } + } + myAnnotationProcessingProfileMap = map; + } + final AnnotationProcessingProfile profile = map.get(module); + return profile != null? profile : compilerConfig.getDefaultAnnotationProcessingProfile(); + } + public void markDirty(final File file) throws IOException { final RootDescriptor descriptor = getModuleAndRoot(file); if (descriptor != null) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 556898df5c1a..139f1bf4858a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -60,7 +60,7 @@ public class JavaBuilder extends ModuleLevelBuilder { public static final boolean USE_EMBEDDED_JAVAC = System.getProperty(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null; private static final Key JAVA_COMPILER_VERSION_KEY = Key.create("_java_compiler_version_"); private static final Set FILTERED_OPTIONS = new HashSet(Arrays.asList( - "-target" + "-target", "-proc:none", "-proc:only" )); private static final Set FILTERED_SINGLE_OPTIONS = new HashSet(Arrays.asList( "-g", "-deprecation", "-nowarn", "-verbose" @@ -399,6 +399,9 @@ public class JavaBuilder extends ModuleLevelBuilder { DiagnosticOutputConsumer diagnosticSink, final OutputFileConsumer outputSink) throws Exception { final List options = getCompilationOptions(context, chunk); + if (context.errorsDetected()) { + return true; + } final ClassProcessingConsumer classesConsumer = new ClassProcessingConsumer(context, outputSink); try { final boolean rc; @@ -670,9 +673,57 @@ public class JavaBuilder extends ModuleLevelBuilder { } } + AnnotationProcessingProfile profile = null; + for (Module module : chunk.getModules()) { + if (profile == null) { + profile = context.getAnnotationProcessingProfile(module); + } + else { + final AnnotationProcessingProfile profile2 = context.getAnnotationProcessingProfile(module); + if (profile2 != profile) { + String message = "Modules in cycle [" + getChunkPresentableName(chunk) + "] must use the same annotation processing profile"; + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message)); + } + } + } + + if (profile != null && profile.isEnabled()) { + // configuring annotation processing + if (!profile.getObtainProcessorsFromClasspath()) { + final String processorsPath = profile.getProcessorsPath(); + options.add("-processorpath"); + options.add(processorsPath == null? "" : FileUtil.toSystemDependentName(processorsPath.trim())); + } + + for (String procFQName : profile.getProcessors()) { + options.add("-processor"); + options.add(procFQName); + } + + for (Map.Entry optionEntry : profile.getProcessorsOptions().entrySet()) { + options.add("-A" + optionEntry.getKey() + "=" + optionEntry.getValue()); + } + + final File srcOutput = getGeneratedSourcesOutputDirectory(context, chunk, profile.getGeneratedSourcesDirName()); + if (srcOutput != null) { + srcOutput.mkdirs(); + options.add("-s"); + options.add(srcOutput.getPath()); + } + } + else { + options.add("-proc:none"); + } + return options; } + @Nullable + private static File getGeneratedSourcesOutputDirectory(CompileContext context, ModuleChunk chunk, String name) { + // todo: support multiple outputs for module chunk + return context.getProjectPaths().getAnnotationProcessorGeneratedSourcesOutputDir(chunk.getModules().iterator().next(), context.isCompilingTests(), name); + } + private static boolean isEncodingSet(List options) { for (String option : options) { if ("-encoding".equals(option)) { @@ -718,7 +769,8 @@ public class JavaBuilder extends ModuleLevelBuilder { //options.add("-verbose"); final Project project = context.getProject(); - final Map javacOpts = project.getCompilerConfiguration().getJavacOptions(); + final CompilerConfiguration compilerConfig = project.getCompilerConfiguration(); + final Map javacOpts = compilerConfig.getJavacOptions(); final boolean debugInfo = !"false".equals(javacOpts.get("DEBUGGING_INFO")); final boolean nowarn = "true".equals(javacOpts.get("GENERATE_NO_WARNINGS")); final boolean deprecation = !"false".equals(javacOpts.get("DEPRECATION")); @@ -778,6 +830,9 @@ public class JavaBuilder extends ModuleLevelBuilder { private static void instrumentNotNull(CompileContext context, OutputFilesSink sink, final InstrumentationClassFinder finder) { for (final OutputFileObject fileObject : sink.getFileObjects()) { final OutputFileObject.Content originalContent = fileObject.getContent(); + if (originalContent == null || !JavaFileObject.Kind.CLASS.equals(fileObject.getKind())) { + continue; + } final ClassReader reader = new ClassReader(originalContent.getBuffer(), originalContent.getOffset(), originalContent.getLength()); final int version = getClassFileVersion(reader); if (version >= Opcodes.V1_5) { @@ -822,7 +877,9 @@ public class JavaBuilder extends ModuleLevelBuilder { final Map compiledClassNames = new HashMap(); for (OutputFileObject fileObject : outputSink.getFileObjects()) { - compiledClassNames.put(fileObject.getClassName(), fileObject); + if (JavaFileObject.Kind.CLASS.equals(fileObject.getKind())) { + compiledClassNames.put(fileObject.getClassName(), fileObject); + } } final MyNestedFormLoader nestedFormsLoader = diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java index 588d0da4620f..f74b0bab3984 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java @@ -19,7 +19,7 @@ public class JavacMain { "-d", "-classpath", "-cp", "-bootclasspath" )); private static final Set FILTERED_SINGLE_OPTIONS = new HashSet(Arrays.asList( - "-verbose", "-proc:none", "-implicit:class", "-implicit:none" + "-verbose", "-proc:only", "-implicit:class", "-implicit:none" )); public static boolean compile(Collection options, @@ -71,12 +71,12 @@ public class JavacMain { out, fileManager, outConsumer, _options, null, fileManager.toJavaFileObjects(sources) ); - if (!IS_VM_6_VERSION) { - // Do not add the processor for JDK 1.6 because of the bugs in javac - // The processor's presence may lead to NPE and resolve bugs in compiler - final JavacASTAnalyser analyzer = new JavacASTAnalyser(outConsumer, shouldSuppressAnnotationProcessing(options)); - task.setProcessors(Collections.singleton(analyzer)); - } + //if (!IS_VM_6_VERSION) { //todo! + // // Do not add the processor for JDK 1.6 because of the bugs in javac + // // The processor's presence may lead to NPE and resolve bugs in compiler + // final JavacASTAnalyser analyzer = new JavacASTAnalyser(outConsumer, !annotationProcessingEnabled); + // task.setProcessors(Collections.singleton(analyzer)); + //} return task.call(); } catch(IllegalArgumentException e) { @@ -88,13 +88,13 @@ public class JavacMain { return false; } - private static boolean shouldSuppressAnnotationProcessing(final Collection options) { + private static boolean isAnnotationProcessingEnabled(final Collection options) { for (String option : options) { if ("-proc:none".equals(option)) { - return true; + return false; } } - return false; + return true; } private static Collection prepareOptions(final Collection options) { diff --git a/jps/model/src/org/jetbrains/jps/CompilerConfiguration.groovy b/jps/model/src/org/jetbrains/jps/CompilerConfiguration.groovy index 826087af3ada..1dc8b34ebd41 100644 --- a/jps/model/src/org/jetbrains/jps/CompilerConfiguration.groovy +++ b/jps/model/src/org/jetbrains/jps/CompilerConfiguration.groovy @@ -10,17 +10,21 @@ class CompilerConfiguration { Map javacOptions = [:] boolean clearOutputDirectoryOnRebuild = true boolean addNotNullAssertions = true - AnnotationProcessingConfiguration annotationProcessing = new AnnotationProcessingConfiguration() + AnnotationProcessingProfile defaultAnnotationProcessingProfile = new AnnotationProcessingProfile() + Collection moduleAnnotationProcessingProfiles = [] BytecodeTargetConfiguration bytecodeTarget = new BytecodeTargetConfiguration() CompilerExcludes excludes = new CompilerExcludes() } -class AnnotationProcessingConfiguration { +class AnnotationProcessingProfile { + String name = "" boolean enabled = false boolean obtainProcessorsFromClasspath = true String processorsPath + List processors = [] Map processorsOptions = [:] - Map processModule = [:] + String generatedSourcesDirName = "" + List processModule = [] } class BytecodeTargetConfiguration { diff --git a/jps/model/src/org/jetbrains/jps/ProjectPaths.java b/jps/model/src/org/jetbrains/jps/ProjectPaths.java index f4eb61024a37..cac4d2399d0d 100644 --- a/jps/model/src/org/jetbrains/jps/ProjectPaths.java +++ b/jps/model/src/org/jetbrains/jps/ProjectPaths.java @@ -1,6 +1,7 @@ package org.jetbrains.jps; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,6 +13,7 @@ import java.util.*; * Date: 9/30/11 */ public class ProjectPaths { + private static final String DEFAULT_GENERATED_DIR_NAME = "generated"; @NotNull private final Project myProject; @Nullable @@ -236,6 +238,36 @@ public class ProjectPaths { return path != null ? new File(path) : null; } + @Nullable + public File getAnnotationProcessorGeneratedSourcesOutputDir(Module module, final boolean forTests, String sourceDirName) { + if (!StringUtil.isEmpty(sourceDirName)) { + List roots = module.getContentRoots(); + if (roots.isEmpty()) { + return null; + } + if (roots.size() > 1) { + roots = new ArrayList(roots); // sort roots to get deterministic result + Collections.sort(roots, new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } + }); + } + return new File(roots.get(0), sourceDirName); + } + + final File outputDir = getModuleOutputDir(module, forTests); + if (outputDir == null) { + return null; + } + final File parentFile = outputDir.getParentFile(); + if (parentFile == null) { + return null; + } + return new File(parentFile, outputDir.getName() + "_" + DEFAULT_GENERATED_DIR_NAME); + } + public List getProjectRuntimeClasspath(boolean includeTests) { Set classpath = new LinkedHashSet(); final ClasspathKind kind = ClasspathKind.runtime(includeTests); diff --git a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy index 92521fb12f09..51f09e2dba5f 100644 --- a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy +++ b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy @@ -251,18 +251,36 @@ public class IdeaProjectLoader { def annotationProcessingTag = componentTag?.annotationProcessing if (annotationProcessingTag != null) { - configuration.annotationProcessing.enabled = parseBoolean(annotationProcessingTag."@enabled", false) - configuration.annotationProcessing.obtainProcessorsFromClasspath = parseBoolean(annotationProcessingTag."@useClasspath", true) - List processorPaths = [] - annotationProcessingTag.processorPath?.each { - processorPaths << projectMacroExpander.expandMacros(it."@value") - } - configuration.annotationProcessing.processorsPath = processorPaths.join(File.pathSeparator) - annotationProcessingTag.processor?.each { - configuration.annotationProcessing.processorsOptions[it."@name"] = it."@options" ?: "" - } - annotationProcessingTag.processModule?.each { - configuration.annotationProcessing.processModule[it."@name"] = it."@generatedDirName" + configuration.moduleAnnotationProcessingProfiles = [] + annotationProcessingTag?.profile?.each {profileTag -> + AnnotationProcessingProfile profile + if (parseBoolean(profileTag."@default", false)) { + profile = configuration.defaultAnnotationProcessingProfile + } + else { + profile = new AnnotationProcessingProfile() + configuration.moduleAnnotationProcessingProfiles << profile + } + profile.name = profileTag."@name"; + profile.enabled = parseBoolean(profileTag."@enabled", false) + profile.generatedSourcesDirName = profileTag.sourceOutputDir?.getAt(0)?."@name" + profileTag.processor?.each { + profile.processors << it."@name" + } + profileTag.option?.each { + profile.processorsOptions[it."@name"] = it."@value" ?: "" + } + def pathTag = profileTag.processorPath?.getAt(0) + profile.obtainProcessorsFromClasspath = parseBoolean(pathTag?."@useClasspath", true) + List processorPaths = [] + pathTag?.each { + processorPaths << projectMacroExpander.expandMacros(it."@name") + } + profile.processorsPath = processorPaths.join(File.pathSeparator) + + profileTag.module?.each { + profile.processModule << it."@name" + } } } diff --git a/jps/testData/compilerConfiguration/compilerConfiguration.ipr b/jps/testData/compilerConfiguration/compilerConfiguration.ipr index 8cbb9fe84e58..725f7f95afaf 100644 --- a/jps/testData/compilerConfiguration/compilerConfiguration.ipr +++ b/jps/testData/compilerConfiguration/compilerConfiguration.ipr @@ -20,10 +20,16 @@ - - - - + + + + diff --git a/jps/testData/compilerConfigurationDir/.idea/compiler.xml b/jps/testData/compilerConfigurationDir/.idea/compiler.xml index 3303bec62d05..e9222f77bc4d 100644 --- a/jps/testData/compilerConfigurationDir/.idea/compiler.xml +++ b/jps/testData/compilerConfigurationDir/.idea/compiler.xml @@ -20,10 +20,16 @@ - - - - + + + + diff --git a/jps/testSrc/org/jetbrains/jps/CompilerConfigurationTest.groovy b/jps/testSrc/org/jetbrains/jps/CompilerConfigurationTest.groovy index 70763e5a80f5..d2d5eb1b9a8e 100644 --- a/jps/testSrc/org/jetbrains/jps/CompilerConfigurationTest.groovy +++ b/jps/testSrc/org/jetbrains/jps/CompilerConfigurationTest.groovy @@ -33,11 +33,12 @@ class CompilerConfigurationTest extends JpsBuildTestCase { String basePath = project.modules["compilerConfiguration"].basePath assertFalse(configuration.clearOutputDirectoryOnRebuild) assertFalse(configuration.addNotNullAssertions) - assertTrue(configuration.annotationProcessing.enabled) - assertFalse(configuration.annotationProcessing.obtainProcessorsFromClasspath) - assertEquals("$basePath/src", configuration.annotationProcessing.processorsPath) - assertEquals("a=b c=d", configuration.annotationProcessing.processorsOptions["my.proc"]) - assertEquals("gen", configuration.annotationProcessing.processModule["compilerConfiguration"]) + assertTrue(configuration.defaultAnnotationProcessingProfile.enabled) + assertFalse(configuration.defaultAnnotationProcessingProfile.obtainProcessorsFromClasspath) + assertEquals("$basePath/src", configuration.defaultAnnotationProcessingProfile.processorsPath) + assertEquals("b", configuration.defaultAnnotationProcessingProfile.processorsOptions["a"]) + assertEquals("d", configuration.defaultAnnotationProcessingProfile.processorsOptions["c"]) + assertEquals("gen", configuration.defaultAnnotationProcessingProfile.generatedSourcesDirName) assertFalse(configuration.excludes.isExcluded(new File("$basePath/src/nonrec/x/Y.java"))) assertTrue(configuration.excludes.isExcluded(new File("$basePath/src/nonrec/Y.java"))) assertTrue(configuration.excludes.isExcluded(new File("$basePath/src/rec/x/Y.java")))