| /* |
| * Copyright 2000-2014 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. |
| */ |
| |
| /** |
| * created at Jan 3, 2002 |
| * @author Jeka |
| */ |
| package com.intellij.compiler; |
| |
| import com.intellij.CommonBundle; |
| import com.intellij.ProjectTopics; |
| import com.intellij.compiler.impl.javaCompiler.BackendCompiler; |
| import com.intellij.compiler.impl.javaCompiler.eclipse.EclipseCompiler; |
| import com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ApplicationNamesInfo; |
| import com.intellij.openapi.compiler.CompilerBundle; |
| import com.intellij.openapi.compiler.CompilerManager; |
| import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration; |
| import com.intellij.openapi.components.*; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.fileTypes.FileType; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.project.ModuleAdapter; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.ui.InputValidator; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.util.*; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.packaging.artifacts.Artifact; |
| import com.intellij.packaging.impl.artifacts.ArtifactBySourceFileFinder; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.messages.MessageBusConnection; |
| import org.apache.oro.text.regex.*; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile; |
| import org.jetbrains.jps.model.java.impl.compiler.ProcessorConfigProfileImpl; |
| import org.jetbrains.jps.model.serialization.java.compiler.AnnotationProcessorProfileSerializer; |
| import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerConfigurationSerializer; |
| |
| import java.io.File; |
| import java.util.*; |
| |
| @State( |
| name = "CompilerConfiguration", |
| storages = { |
| @Storage(file = StoragePathMacros.PROJECT_FILE), |
| @Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED) |
| } |
| ) |
| public class CompilerConfigurationImpl extends CompilerConfiguration implements PersistentStateComponent<Element>, ProjectComponent { |
| 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"; |
| |
| @SuppressWarnings({"WeakerAccess"}) public String DEFAULT_COMPILER; |
| @NotNull private BackendCompiler myDefaultJavaCompiler; |
| |
| // extensions of the files considered as resource files |
| private final List<Pattern> myRegexpResourcePatterns = new ArrayList<Pattern>(); |
| // extensions of the files considered as resource files. If present, overrides patterns in old regexp format stored in myRegexpResourcePatterns |
| private final List<String> myWildcardPatterns = new ArrayList<String>(); |
| private final List<CompiledPattern> myCompiledPatterns = new ArrayList<CompiledPattern>(); |
| private final List<CompiledPattern> myNegatedCompiledPatterns = new ArrayList<CompiledPattern>(); |
| private boolean myWildcardPatternsInitialized = false; |
| private final Project myProject; |
| private final ExcludedEntriesConfiguration myExcludedEntriesConfiguration; |
| |
| private final Collection<BackendCompiler> myRegisteredCompilers = new ArrayList<BackendCompiler>(); |
| private JavacCompiler JAVAC_EXTERNAL_BACKEND; |
| private final Perl5Matcher myPatternMatcher = new Perl5Matcher(); |
| |
| { |
| loadDefaultWildcardPatterns(); |
| } |
| private boolean myAddNotNullAssertions = true; |
| |
| private final ProcessorConfigProfile myDefaultProcessorsProfile = new ProcessorConfigProfileImpl("Default"); |
| private final List<ProcessorConfigProfile> myModuleProcessorProfiles = new ArrayList<ProcessorConfigProfile>(); |
| |
| // the map is calculated by module processor profiles list for faster access to module settings |
| private Map<Module, ProcessorConfigProfile> myProcessorsProfilesMap = null; |
| |
| @Nullable |
| private String myBytecodeTargetLevel = null; // null means compiler default |
| private final Map<String, String> myModuleBytecodeTarget = new java.util.HashMap<String, String>(); |
| |
| public CompilerConfigurationImpl(Project project) { |
| myProject = project; |
| myExcludedEntriesConfiguration = new ExcludedEntriesConfiguration(); |
| Disposer.register(project, myExcludedEntriesConfiguration); |
| MessageBusConnection connection = project.getMessageBus().connect(project); |
| connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() { |
| public void beforeModuleRemoved(Project project, Module module) { |
| getAnnotationProcessingConfiguration(module).removeModuleName(module.getName()); |
| } |
| |
| public void moduleAdded(Project project, Module module) { |
| myProcessorsProfilesMap = null; // clear cache |
| } |
| }); |
| } |
| |
| public Element getState() { |
| try { |
| @NonNls final Element e = new Element("state"); |
| writeExternal(e); |
| return e; |
| } |
| catch (WriteExternalException e1) { |
| LOG.error(e1); |
| return null; |
| } |
| } |
| |
| public void loadState(Element state) { |
| try { |
| readExternal(state); |
| } |
| catch (InvalidDataException e) { |
| LOG.error(e); |
| } |
| } |
| |
| public void setProjectBytecodeTarget(@Nullable String level) { |
| myBytecodeTargetLevel = level; |
| } |
| |
| @Override |
| @Nullable |
| public String getProjectBytecodeTarget() { |
| return myBytecodeTargetLevel; |
| } |
| |
| public void setModulesBytecodeTargetMap(@NotNull Map<String, String> mapping) { |
| myModuleBytecodeTarget.clear(); |
| myModuleBytecodeTarget.putAll(mapping); |
| } |
| |
| public Map<String, String> getModulesBytecodeTargetMap() { |
| return myModuleBytecodeTarget; |
| } |
| |
| public void setBytecodeTargetLevel(Module module, String level) { |
| final String previous; |
| if (StringUtil.isEmpty(level)) { |
| previous = myModuleBytecodeTarget.remove(module.getName()); |
| } |
| else { |
| previous = myModuleBytecodeTarget.put(module.getName(), level); |
| } |
| // todo: mark module as dirty in order to rebuild it completely with the new target level |
| //if (!Comparing.equal(previous, level)) { |
| // final Project project = module.getProject(); |
| // |
| //} |
| } |
| |
| @Override |
| @Nullable |
| public String getBytecodeTargetLevel(Module module) { |
| final String level = myModuleBytecodeTarget.get(module.getName()); |
| if (level != null) { |
| return level.isEmpty() ? null : level; |
| } |
| return myBytecodeTargetLevel; |
| } |
| |
| private void loadDefaultWildcardPatterns() { |
| if (!myWildcardPatterns.isEmpty()) { |
| removeWildcardPatterns(); |
| } |
| try { |
| addWildcardResourcePattern("!?*.java"); |
| addWildcardResourcePattern("!?*.form"); |
| addWildcardResourcePattern("!?*.class"); |
| addWildcardResourcePattern("!?*.groovy"); |
| addWildcardResourcePattern("!?*.scala"); |
| addWildcardResourcePattern("!?*.flex"); |
| addWildcardResourcePattern("!?*.kt"); |
| addWildcardResourcePattern("!?*.clj"); |
| } |
| catch (MalformedPatternException e) { |
| LOG.error(e); |
| } |
| } |
| |
| public static String getTestsExternalCompilerHome() { |
| String compilerHome = System.getProperty(TESTS_EXTERNAL_COMPILER_HOME_PROPERTY_NAME, null); |
| if (compilerHome == null) { |
| if (SystemInfo.isMac) { |
| compilerHome = new File(System.getProperty("java.home")).getAbsolutePath(); |
| } |
| else { |
| compilerHome = new File(System.getProperty("java.home")).getParentFile().getAbsolutePath(); |
| } |
| } |
| return compilerHome; |
| } |
| |
| private static Pattern compilePattern(@NonNls String s) throws MalformedPatternException { |
| try { |
| final PatternCompiler compiler = new Perl5Compiler(); |
| return SystemInfo.isFileSystemCaseSensitive? compiler.compile(s) : compiler.compile(s, Perl5Compiler.CASE_INSENSITIVE_MASK); |
| } |
| catch (org.apache.oro.text.regex.MalformedPatternException ex) { |
| throw new MalformedPatternException(ex); |
| } |
| } |
| |
| public void disposeComponent() { |
| } |
| |
| public void initComponent() { } |
| |
| public void projectClosed() { |
| } |
| |
| public JavacCompiler getJavacCompiler() { |
| createCompilers(); |
| return JAVAC_EXTERNAL_BACKEND; |
| } |
| |
| public void projectOpened() { |
| createCompilers(); |
| } |
| |
| private void createCompilers() { |
| if (JAVAC_EXTERNAL_BACKEND != null) { |
| return; |
| } |
| |
| JAVAC_EXTERNAL_BACKEND = new JavacCompiler(myProject); |
| myRegisteredCompilers.add(JAVAC_EXTERNAL_BACKEND); |
| |
| if (!ApplicationManager.getApplication().isUnitTestMode()) { |
| if (EclipseCompiler.isInitialized()) { |
| final EclipseCompiler eclipse = new EclipseCompiler(myProject); |
| myRegisteredCompilers.add(eclipse); |
| } |
| } |
| |
| final Set<FileType> types = new HashSet<FileType>(); |
| for (BackendCompiler compiler : Extensions.getExtensions(BackendCompiler.EP_NAME, myProject)) { |
| myRegisteredCompilers.add(compiler); |
| types.addAll(compiler.getCompilableFileTypes()); |
| } |
| |
| final CompilerManager compilerManager = CompilerManager.getInstance(myProject); |
| for (FileType type : types) { |
| compilerManager.addCompilableFileType(type); |
| } |
| |
| myDefaultJavaCompiler = JAVAC_EXTERNAL_BACKEND; |
| for (BackendCompiler compiler : myRegisteredCompilers) { |
| if (compiler.getId().equals(DEFAULT_COMPILER)) { |
| myDefaultJavaCompiler = compiler; |
| break; |
| } |
| } |
| DEFAULT_COMPILER = myDefaultJavaCompiler.getId(); |
| } |
| |
| public Collection<BackendCompiler> getRegisteredJavaCompilers() { |
| createCompilers(); |
| return myRegisteredCompilers; |
| } |
| |
| public String[] getResourceFilePatterns() { |
| return getWildcardPatterns(); |
| } |
| |
| private String[] getRegexpPatterns() { |
| String[] patterns = ArrayUtil.newStringArray(myRegexpResourcePatterns.size()); |
| int index = 0; |
| for (final Pattern myRegexpResourcePattern : myRegexpResourcePatterns) { |
| patterns[index++] = myRegexpResourcePattern.getPattern(); |
| } |
| return patterns; |
| } |
| |
| private String[] getWildcardPatterns() { |
| return ArrayUtil.toStringArray(myWildcardPatterns); |
| } |
| |
| public void addResourceFilePattern(String namePattern) throws MalformedPatternException { |
| addWildcardResourcePattern(namePattern); |
| } |
| |
| // need this method only for handling patterns in old regexp format |
| private void addRegexpPattern(String namePattern) throws MalformedPatternException { |
| Pattern pattern = compilePattern(namePattern); |
| if (pattern != null) { |
| myRegexpResourcePatterns.add(pattern); |
| } |
| } |
| |
| @Override |
| public ExcludedEntriesConfiguration getExcludedEntriesConfiguration() { |
| return myExcludedEntriesConfiguration; |
| } |
| |
| public boolean isExcludedFromCompilation(final VirtualFile virtualFile) { |
| return myExcludedEntriesConfiguration.isExcluded(virtualFile); |
| } |
| |
| @Override |
| public boolean isResourceFile(VirtualFile virtualFile) { |
| return isResourceFile(virtualFile.getName(), virtualFile.getParent()); |
| } |
| |
| @Override |
| public boolean isAddNotNullAssertions() { |
| return myAddNotNullAssertions; |
| } |
| |
| @Override |
| public void setAddNotNullAssertions(boolean enabled) { |
| myAddNotNullAssertions = enabled; |
| } |
| |
| @NotNull |
| public ProcessorConfigProfile getDefaultProcessorProfile() { |
| return myDefaultProcessorsProfile; |
| } |
| |
| public void setDefaultProcessorProfile(ProcessorConfigProfile profile) { |
| myDefaultProcessorsProfile.initFrom(profile); |
| } |
| |
| @NotNull |
| public List<ProcessorConfigProfile> getModuleProcessorProfiles() { |
| return myModuleProcessorProfiles; |
| } |
| |
| public void setModuleProcessorProfiles(Collection<ProcessorConfigProfile> moduleProfiles) { |
| myModuleProcessorProfiles.clear(); |
| myModuleProcessorProfiles.addAll(moduleProfiles); |
| myProcessorsProfilesMap = null; |
| } |
| |
| @Nullable |
| public ProcessorConfigProfile findModuleProcessorProfile(@NotNull String name) { |
| for (ProcessorConfigProfile profile : myModuleProcessorProfiles) { |
| if (name.equals(profile.getName())) { |
| return profile; |
| } |
| } |
| |
| return null; |
| } |
| |
| public void removeModuleProcessorProfile(ProcessorConfigProfile profile) { |
| myModuleProcessorProfiles.remove(profile); |
| myProcessorsProfilesMap = null; // clear cache |
| } |
| |
| public void addModuleProcessorProfile(@NotNull ProcessorConfigProfile profile) { |
| myModuleProcessorProfiles.add(profile); |
| myProcessorsProfilesMap = null; // clear cache |
| } |
| |
| @Override |
| @NotNull |
| public ProcessorConfigProfile getAnnotationProcessingConfiguration(Module module) { |
| Map<Module, ProcessorConfigProfile> map = myProcessorsProfilesMap; |
| if (map == null) { |
| map = new HashMap<Module, ProcessorConfigProfile>(); |
| final Map<String, Module> namesMap = new HashMap<String, Module>(); |
| 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() { |
| 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 { |
| final CompiledPattern pattern = convertToRegexp(wildcardPattern); |
| if (pattern != null) { |
| myWildcardPatterns.add(wildcardPattern); |
| if (isPatternNegated(wildcardPattern)) { |
| myNegatedCompiledPatterns.add(pattern); |
| } |
| else { |
| myCompiledPatterns.add(pattern); |
| } |
| } |
| } |
| |
| public void removeResourceFilePatterns() { |
| removeWildcardPatterns(); |
| } |
| |
| private void removeRegexpPatterns() { |
| myRegexpResourcePatterns.clear(); |
| } |
| |
| private void removeWildcardPatterns() { |
| myWildcardPatterns.clear(); |
| myCompiledPatterns.clear(); |
| myNegatedCompiledPatterns.clear(); |
| } |
| |
| private static CompiledPattern convertToRegexp(String wildcardPattern) { |
| if (isPatternNegated(wildcardPattern)) { |
| wildcardPattern = wildcardPattern.substring(1); |
| } |
| |
| wildcardPattern = FileUtil.toSystemIndependentName(wildcardPattern); |
| |
| String srcRoot = null; |
| int colon = wildcardPattern.indexOf(":"); |
| if (colon > 0) { |
| srcRoot = wildcardPattern.substring(0, colon); |
| wildcardPattern = wildcardPattern.substring(colon + 1); |
| } |
| |
| String dirPattern = null; |
| int slash = wildcardPattern.lastIndexOf('/'); |
| if (slash >= 0) { |
| dirPattern = wildcardPattern.substring(0, slash + 1); |
| wildcardPattern = wildcardPattern.substring(slash + 1); |
| if (!dirPattern.startsWith("/")) { |
| dirPattern = "/" + dirPattern; |
| } |
| //now dirPattern starts and ends with '/' |
| |
| dirPattern = normalizeWildcards(dirPattern); |
| |
| dirPattern = StringUtil.replace(dirPattern, "/.*.*/", "(/.*)?/"); |
| dirPattern = StringUtil.trimEnd(dirPattern, "/"); |
| |
| dirPattern = optimize(dirPattern); |
| } |
| |
| wildcardPattern = normalizeWildcards(wildcardPattern); |
| wildcardPattern = optimize(wildcardPattern); |
| |
| final Pattern dirCompiled = dirPattern == null ? null : compilePattern(dirPattern); |
| final Pattern srcCompiled = srcRoot == null ? null : compilePattern(optimize(normalizeWildcards(srcRoot))); |
| return new CompiledPattern(compilePattern(wildcardPattern), dirCompiled, srcCompiled); |
| } |
| |
| private static String optimize(String wildcardPattern) { |
| return wildcardPattern.replaceAll("(?:\\.\\*)+", ".*"); |
| } |
| |
| private static String normalizeWildcards(String wildcardPattern) { |
| wildcardPattern = StringUtil.replace(wildcardPattern, "\\!", "!"); |
| wildcardPattern = StringUtil.replace(wildcardPattern, ".", "\\."); |
| wildcardPattern = StringUtil.replace(wildcardPattern, "*?", ".+"); |
| wildcardPattern = StringUtil.replace(wildcardPattern, "?*", ".+"); |
| wildcardPattern = StringUtil.replace(wildcardPattern, "*", ".*"); |
| wildcardPattern = StringUtil.replace(wildcardPattern, "?", "."); |
| return wildcardPattern; |
| } |
| |
| public static boolean isPatternNegated(String wildcardPattern) { |
| return wildcardPattern.length() > 1 && wildcardPattern.charAt(0) == '!'; |
| } |
| |
| public boolean isResourceFile(String name) { |
| return isResourceFile(name, null); |
| } |
| |
| private boolean matches(String s, Pattern p) { |
| synchronized (myPatternMatcher) { |
| try { |
| return myPatternMatcher.matches(s, p); |
| } |
| catch (Exception e) { |
| LOG.error("Exception matching file name \"" + s + "\" against the pattern \"" + p + "\"", e); |
| return false; |
| } |
| } |
| } |
| |
| private boolean isResourceFile(String name, @Nullable VirtualFile parent) { |
| final Ref<String> parentRef = Ref.create(null); |
| //noinspection ForLoopReplaceableByForEach |
| for (int i = 0; i < myCompiledPatterns.size(); i++) { |
| if (matches(name, parent, parentRef, myCompiledPatterns.get(i))) { |
| return true; |
| } |
| } |
| |
| if (myNegatedCompiledPatterns.isEmpty()) { |
| return false; |
| } |
| |
| //noinspection ForLoopReplaceableByForEach |
| for (int i = 0; i < myNegatedCompiledPatterns.size(); i++) { |
| if (matches(name, parent, parentRef, myNegatedCompiledPatterns.get(i))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean matches(String name, VirtualFile parent, Ref<String> parentRef, CompiledPattern pair) { |
| if (!matches(name, pair.fileName)) { |
| return false; |
| } |
| |
| if (parent != null && (pair.dir != null || pair.srcRoot != null)) { |
| VirtualFile srcRoot = ProjectRootManager.getInstance(myProject).getFileIndex().getSourceRootForFile(parent); |
| if (pair.dir != null) { |
| String parentPath = parentRef.get(); |
| if (parentPath == null) { |
| parentRef.set(parentPath = srcRoot == null ? parent.getPath() : VfsUtilCore.getRelativePath(parent, srcRoot, '/')); |
| } |
| if (parentPath == null || !matches("/" + parentPath, pair.dir)) { |
| return false; |
| } |
| } |
| |
| if (pair.srcRoot != null) { |
| String srcRootName = srcRoot == null ? null : srcRoot.getName(); |
| if (srcRootName == null || !matches(srcRootName, pair.srcRoot)) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| |
| public void readExternal(Element parentNode) throws InvalidDataException { |
| DefaultJDOMExternalizer.readExternal(this, parentNode); |
| |
| final Element notNullAssertions = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.ADD_NOTNULL_ASSERTIONS); |
| if (notNullAssertions != null) { |
| myAddNotNullAssertions = Boolean.valueOf(notNullAssertions.getAttributeValue(JpsJavaCompilerConfigurationSerializer.ENABLED, "true")); |
| } |
| |
| Element node = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.EXCLUDE_FROM_COMPILE); |
| if (node != null) { |
| myExcludedEntriesConfiguration.readExternal(node); |
| } |
| |
| try { |
| removeRegexpPatterns(); |
| node = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.RESOURCE_EXTENSIONS); |
| if (node != null) { |
| for (final Object o : node.getChildren(JpsJavaCompilerConfigurationSerializer.ENTRY)) { |
| Element element = (Element)o; |
| String pattern = element.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME); |
| if (!StringUtil.isEmpty(pattern)) { |
| addRegexpPattern(pattern); |
| } |
| } |
| } |
| |
| removeWildcardPatterns(); |
| node = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.WILDCARD_RESOURCE_PATTERNS); |
| if (node != null) { |
| myWildcardPatternsInitialized = true; |
| for (final Object o : node.getChildren(JpsJavaCompilerConfigurationSerializer.ENTRY)) { |
| final Element element = (Element)o; |
| String pattern = element.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME); |
| if (!StringUtil.isEmpty(pattern)) { |
| addWildcardResourcePattern(pattern); |
| } |
| } |
| } |
| } |
| catch (MalformedPatternException e) { |
| throw new InvalidDataException(e); |
| } |
| |
| |
| myModuleProcessorProfiles.clear(); |
| myProcessorsProfilesMap = null; |
| |
| final Element annotationProcessingSettings = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.ANNOTATION_PROCESSING); |
| if (annotationProcessingSettings != null) { |
| final List profiles = annotationProcessingSettings.getChildren("profile"); |
| if (!profiles.isEmpty()) { |
| for (Object elem : profiles) { |
| final Element profileElement = (Element)elem; |
| final boolean isDefault = "true".equals(profileElement.getAttributeValue("default")); |
| if (isDefault) { |
| AnnotationProcessorProfileSerializer.readExternal(myDefaultProcessorsProfile, profileElement); |
| } |
| else { |
| final ProcessorConfigProfile profile = new ProcessorConfigProfileImpl(""); |
| AnnotationProcessorProfileSerializer.readExternal(profile, profileElement); |
| myModuleProcessorProfiles.add(profile); |
| } |
| } |
| } |
| else { |
| // assuming older format |
| loadProfilesFromOldFormat(annotationProcessingSettings); |
| } |
| } |
| |
| myBytecodeTargetLevel = null; |
| myModuleBytecodeTarget.clear(); |
| final Element bytecodeTargetElement = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL); |
| if (bytecodeTargetElement != null) { |
| myBytecodeTargetLevel = bytecodeTargetElement.getAttributeValue(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE); |
| for (Element elem : (Collection<Element>)bytecodeTargetElement.getChildren(JpsJavaCompilerConfigurationSerializer.MODULE)) { |
| final String name = elem.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME); |
| if (name == null) { |
| continue; |
| } |
| final String target = elem.getAttributeValue(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE); |
| if (target == null) { |
| continue; |
| } |
| myModuleBytecodeTarget.put(name, target); |
| } |
| } |
| } |
| |
| private void loadProfilesFromOldFormat(Element processing) { |
| // collect data |
| final boolean isEnabled = Boolean.parseBoolean(processing.getAttributeValue(JpsJavaCompilerConfigurationSerializer.ENABLED, "false")); |
| final boolean isUseClasspath = Boolean.parseBoolean(processing.getAttributeValue("useClasspath", "true")); |
| final StringBuilder processorPath = new StringBuilder(); |
| final Set<String> optionPairs = new HashSet<String>(); |
| final Set<String> processors = new HashSet<String>(); |
| final List<Couple<String>> modulesToProcess = new ArrayList<Couple<String>>(); |
| |
| for (Object child : processing.getChildren("processorPath")) { |
| final Element pathElement = (Element)child; |
| final String path = pathElement.getAttributeValue("value", (String)null); |
| if (path != null) { |
| if (processorPath.length() > 0) { |
| processorPath.append(File.pathSeparator); |
| } |
| processorPath.append(path); |
| } |
| } |
| |
| for (Object child : processing.getChildren("processor")) { |
| final Element processorElement = (Element)child; |
| final String proc = processorElement.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME, (String)null); |
| if (proc != null) { |
| processors.add(proc); |
| } |
| final StringTokenizer tokenizer = new StringTokenizer(processorElement.getAttributeValue("options", ""), " ", false); |
| while (tokenizer.hasMoreTokens()) { |
| final String pair = tokenizer.nextToken(); |
| optionPairs.add(pair); |
| } |
| } |
| |
| for (Object child : processing.getChildren("processModule")) { |
| final Element moduleElement = (Element)child; |
| final String name = moduleElement.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME, (String)null); |
| if (name == null) { |
| continue; |
| } |
| final String dir = moduleElement.getAttributeValue("generatedDirName", (String)null); |
| modulesToProcess.add(Couple.of(name, dir)); |
| } |
| |
| myDefaultProcessorsProfile.setEnabled(false); |
| myDefaultProcessorsProfile.setObtainProcessorsFromClasspath(isUseClasspath); |
| if (processorPath.length() > 0) { |
| myDefaultProcessorsProfile.setProcessorPath(processorPath.toString()); |
| } |
| if (!optionPairs.isEmpty()) { |
| for (String pair : optionPairs) { |
| final int index = pair.indexOf("="); |
| if (index > 0) { |
| myDefaultProcessorsProfile.setOption(pair.substring(0, index), pair.substring(index + 1)); |
| } |
| } |
| } |
| for (String processor : processors) { |
| myDefaultProcessorsProfile.addProcessor(processor); |
| } |
| |
| final Map<String, Set<String>> dirNameToModulesMap = new HashMap<String, Set<String>>(); |
| for (Couple<String> moduleDirPair : modulesToProcess) { |
| final String dir = moduleDirPair.getSecond(); |
| Set<String> set = dirNameToModulesMap.get(dir); |
| if (set == null) { |
| set = new HashSet<String>(); |
| dirNameToModulesMap.put(dir, set); |
| } |
| set.add(moduleDirPair.getFirst()); |
| } |
| |
| int profileIndex = 0; |
| for (Map.Entry<String, Set<String>> entry : dirNameToModulesMap.entrySet()) { |
| final String dirName = entry.getKey(); |
| final ProcessorConfigProfile profile = new ProcessorConfigProfileImpl(myDefaultProcessorsProfile); |
| profile.setName("Profile" + (++profileIndex)); |
| profile.setEnabled(isEnabled); |
| profile.setGeneratedSourcesDirectoryName(dirName, false); |
| for (String moduleName : entry.getValue()) { |
| profile.addModuleName(moduleName); |
| } |
| myModuleProcessorProfiles.add(profile); |
| } |
| } |
| |
| private void writeExternal(Element parentNode) throws WriteExternalException { |
| DefaultJDOMExternalizer.writeExternal(this, parentNode); |
| |
| if (myAddNotNullAssertions != true) { |
| addChild(parentNode, JpsJavaCompilerConfigurationSerializer.ADD_NOTNULL_ASSERTIONS).setAttribute( |
| JpsJavaCompilerConfigurationSerializer.ENABLED, String.valueOf(myAddNotNullAssertions)); |
| } |
| |
| if(myExcludedEntriesConfiguration.getExcludeEntryDescriptions().length > 0) { |
| myExcludedEntriesConfiguration.writeExternal(addChild(parentNode, JpsJavaCompilerConfigurationSerializer.EXCLUDE_FROM_COMPILE)); |
| } |
| |
| final Element newChild = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.RESOURCE_EXTENSIONS); |
| for (final String pattern : getRegexpPatterns()) { |
| addChild(newChild, JpsJavaCompilerConfigurationSerializer.ENTRY).setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, pattern); |
| } |
| |
| if (myWildcardPatternsInitialized || !myWildcardPatterns.isEmpty()) { |
| final Element wildcardPatterns = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.WILDCARD_RESOURCE_PATTERNS); |
| for (final String wildcardPattern : myWildcardPatterns) { |
| addChild(wildcardPatterns, JpsJavaCompilerConfigurationSerializer.ENTRY).setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, wildcardPattern); |
| } |
| } |
| |
| final Element annotationProcessingSettings = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.ANNOTATION_PROCESSING); |
| final Element defaultProfileElem = addChild(annotationProcessingSettings, "profile").setAttribute("default", "true"); |
| AnnotationProcessorProfileSerializer.writeExternal(myDefaultProcessorsProfile, defaultProfileElem); |
| for (ProcessorConfigProfile profile : myModuleProcessorProfiles) { |
| final Element profileElem = addChild(annotationProcessingSettings, "profile").setAttribute("default", "false"); |
| AnnotationProcessorProfileSerializer.writeExternal(profile, profileElem); |
| } |
| |
| if (!StringUtil.isEmpty(myBytecodeTargetLevel) || !myModuleBytecodeTarget.isEmpty()) { |
| final Element bytecodeTarget = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL); |
| if (!StringUtil.isEmpty(myBytecodeTargetLevel)) { |
| bytecodeTarget.setAttribute(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE, myBytecodeTargetLevel); |
| } |
| if (!myModuleBytecodeTarget.isEmpty()) { |
| final List<String> moduleNames = new ArrayList<String>(myModuleBytecodeTarget.keySet()); |
| Collections.sort(moduleNames, String.CASE_INSENSITIVE_ORDER); |
| for (String name : moduleNames) { |
| final Element moduleElement = addChild(bytecodeTarget, JpsJavaCompilerConfigurationSerializer.MODULE); |
| moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, name); |
| final String value = myModuleBytecodeTarget.get(name); |
| moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE, value != null? value : ""); |
| } |
| } |
| } |
| } |
| |
| @NotNull @NonNls |
| public String getComponentName() { |
| return "CompilerConfiguration"; |
| } |
| |
| public BackendCompiler getDefaultCompiler() { |
| createCompilers(); |
| return myDefaultJavaCompiler; |
| } |
| |
| /** |
| * @param defaultCompiler The compiler that is passed as a parameter to setDefaultCompiler() |
| * must be one of the registered compilers in compiler configuration. |
| * Otherwise because of lazy compiler initialization, the value of default compiler will point to some other compiler instance |
| */ |
| public void setDefaultCompiler(BackendCompiler defaultCompiler) { |
| myDefaultJavaCompiler = defaultCompiler; |
| DEFAULT_COMPILER = defaultCompiler.getId(); |
| } |
| |
| public void convertPatterns() { |
| if (!needPatternConversion()) { |
| return; |
| } |
| try { |
| boolean ok; |
| try { |
| ok = doConvertPatterns(); |
| } |
| catch (MalformedPatternException e) { |
| ok = false; |
| } |
| if (!ok) { |
| final String initialPatternString = patternsToString(getRegexpPatterns()); |
| final String message = CompilerBundle.message( |
| "message.resource.patterns.format.changed", |
| ApplicationNamesInfo.getInstance().getProductName(), |
| initialPatternString, |
| CommonBundle.getOkButtonText(), |
| CommonBundle.getCancelButtonText() |
| ); |
| final String wildcardPatterns = Messages.showInputDialog( |
| myProject, message, CompilerBundle.message("pattern.conversion.dialog.title"), Messages.getWarningIcon(), initialPatternString, new InputValidator() { |
| public boolean checkInput(String inputString) { |
| return true; |
| } |
| public boolean canClose(String inputString) { |
| final StringTokenizer tokenizer = new StringTokenizer(inputString, ";", false); |
| StringBuilder malformedPatterns = new StringBuilder(); |
| |
| while (tokenizer.hasMoreTokens()) { |
| String pattern = tokenizer.nextToken(); |
| try { |
| addWildcardResourcePattern(pattern); |
| } |
| catch (MalformedPatternException e) { |
| malformedPatterns.append("\n\n"); |
| malformedPatterns.append(pattern); |
| malformedPatterns.append(": "); |
| malformedPatterns.append(e.getMessage()); |
| } |
| } |
| |
| if (malformedPatterns.length() > 0) { |
| Messages.showErrorDialog(CompilerBundle.message("error.bad.resource.patterns", malformedPatterns.toString()), |
| CompilerBundle.message("bad.resource.patterns.dialog.title")); |
| removeWildcardPatterns(); |
| return false; |
| } |
| return true; |
| } |
| }); |
| if (wildcardPatterns == null) { // cancel pressed |
| loadDefaultWildcardPatterns(); |
| } |
| } |
| } |
| finally { |
| myWildcardPatternsInitialized = true; |
| } |
| } |
| |
| private boolean needPatternConversion() { |
| return !myWildcardPatternsInitialized && !myRegexpResourcePatterns.isEmpty(); |
| } |
| |
| private boolean doConvertPatterns() throws MalformedPatternException { |
| final String[] regexpPatterns = getRegexpPatterns(); |
| final List<String> converted = new ArrayList<String>(); |
| final Pattern multipleExtensionsPatternPattern = compilePattern("\\.\\+\\\\\\.\\((\\w+(?:\\|\\w+)*)\\)"); |
| final Pattern singleExtensionPatternPattern = compilePattern("\\.\\+\\\\\\.(\\w+)"); |
| final Perl5Matcher matcher = new Perl5Matcher(); |
| for (final String regexpPattern : regexpPatterns) { |
| //final Matcher multipleExtensionsMatcher = multipleExtensionsPatternPattern.matcher(regexpPattern); |
| if (matcher.matches(regexpPattern, multipleExtensionsPatternPattern)) { |
| final MatchResult match = matcher.getMatch(); |
| final StringTokenizer tokenizer = new StringTokenizer(match.group(1), "|", false); |
| while (tokenizer.hasMoreTokens()) { |
| converted.add("?*." + tokenizer.nextToken()); |
| } |
| } |
| else { |
| //final Matcher singleExtensionMatcher = singleExtensionPatternPattern.matcher(regexpPattern); |
| if (matcher.matches(regexpPattern, singleExtensionPatternPattern)) { |
| final MatchResult match = matcher.getMatch(); |
| converted.add("?*." + match.group(1)); |
| } |
| else { |
| return false; |
| } |
| } |
| } |
| for (final String aConverted : converted) { |
| addWildcardResourcePattern(aConverted); |
| } |
| return true; |
| } |
| |
| private static String patternsToString(final String[] patterns) { |
| final StringBuilder extensionsString = new StringBuilder(); |
| for (int idx = 0; idx < patterns.length; idx++) { |
| if (idx > 0) { |
| extensionsString.append(";"); |
| } |
| extensionsString.append(patterns[idx]); |
| } |
| return extensionsString.toString(); |
| } |
| |
| public boolean isCompilableResourceFile(final Project project, final VirtualFile file) { |
| if (!isResourceFile(file)) { |
| return false; |
| } |
| final Collection<? extends Artifact> artifacts = ArtifactBySourceFileFinder.getInstance(project).findArtifacts(file); |
| return artifacts.isEmpty(); |
| } |
| |
| private static class CompiledPattern { |
| @NotNull final Pattern fileName; |
| @Nullable final Pattern dir; |
| @Nullable final Pattern srcRoot; |
| |
| private CompiledPattern(Pattern fileName, Pattern dir, Pattern srcRoot) { |
| this.fileName = fileName; |
| this.dir = dir; |
| this.srcRoot = srcRoot; |
| } |
| } |
| |
| private static Element addChild(Element parent, final String childName) { |
| final Element child = new Element(childName); |
| parent.addContent(child); |
| return child; |
| } |
| |
| } |