| /* |
| * Copyright 2000-2013 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 org.jetbrains.idea.maven.project; |
| |
| import com.intellij.execution.configurations.ParametersList; |
| import com.intellij.openapi.module.ModuleType; |
| import com.intellij.openapi.module.StdModuleTypes; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.util.containers.ContainerUtil; |
| import gnu.trove.THashSet; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.idea.maven.importing.MavenExtraArtifactType; |
| import org.jetbrains.idea.maven.importing.MavenImporter; |
| import org.jetbrains.idea.maven.model.*; |
| import org.jetbrains.idea.maven.plugins.api.MavenModelPropertiesPatcher; |
| import org.jetbrains.idea.maven.server.MavenEmbedderWrapper; |
| import org.jetbrains.idea.maven.server.NativeMavenProjectHolder; |
| import org.jetbrains.idea.maven.utils.*; |
| |
| import java.io.*; |
| import java.util.*; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| public class MavenProject { |
| |
| private static final Key<MavenArtifactIndex> DEPENDENCIES_CACHE_KEY = Key.create("MavenProject.DEPENDENCIES_CACHE_KEY"); |
| private static final Key<List<String>> FILTERS_CACHE_KEY = Key.create("MavenProject.FILTERS_CACHE_KEY"); |
| |
| @NotNull private final VirtualFile myFile; |
| @NotNull private volatile State myState = new State(); |
| |
| private static Map<String, String> COMPILER_LEVEL_TABLE = ContainerUtil.<String,String>immutableMapBuilder() |
| .put("1.1", "1.1") |
| .put("1.2", "1.2") |
| .put("1.3", "1.3") |
| .put("1.4", "1.4") |
| .put("1.5", "1.5") |
| .put("5", "1.5") |
| .put("1.6", "1.6") |
| .put("1.7", "1.7") |
| .put("7", "1.7") |
| .put("1.8", "1.8") |
| .put("8", "1.8") |
| .build(); |
| |
| public enum ProcMode {BOTH, ONLY, NONE} |
| |
| @Nullable |
| public static MavenProject read(DataInputStream in) throws IOException { |
| String path = in.readUTF(); |
| int length = in.readInt(); |
| byte[] bytes = new byte[length]; |
| in.readFully(bytes); |
| |
| // should read full byte content first!!! |
| |
| VirtualFile file = LocalFileSystem.getInstance().findFileByPath(path); |
| if (file == null) return null; |
| |
| ByteArrayInputStream bs = new ByteArrayInputStream(bytes); |
| ObjectInputStream os = new ObjectInputStream(bs); |
| try { |
| try { |
| MavenProject result = new MavenProject(file); |
| result.myState = (State)os.readObject(); |
| return result; |
| } |
| catch (ClassNotFoundException e) { |
| IOException ioException = new IOException(); |
| ioException.initCause(e); |
| throw ioException; |
| } |
| } |
| finally { |
| os.close(); |
| bs.close(); |
| } |
| } |
| |
| public void write(@NotNull DataOutputStream out) throws IOException { |
| out.writeUTF(getPath()); |
| |
| BufferExposingByteArrayOutputStream bs = new BufferExposingByteArrayOutputStream(); |
| ObjectOutputStream os = new ObjectOutputStream(bs); |
| try { |
| os.writeObject(myState); |
| |
| out.writeInt(bs.size()); |
| out.write(bs.getInternalBuffer(), 0, bs.size()); |
| } |
| finally { |
| os.close(); |
| bs.close(); |
| } |
| } |
| |
| public MavenProject(@NotNull VirtualFile file) { |
| myFile = file; |
| } |
| |
| @NotNull |
| private MavenProjectChanges set(@NotNull MavenProjectReaderResult readerResult, |
| @NotNull MavenGeneralSettings settings, |
| boolean updateLastReadStamp, |
| boolean resetArtifacts, |
| boolean resetProfiles) { |
| State newState = myState.clone(); |
| |
| if (updateLastReadStamp) newState.myLastReadStamp = myState.myLastReadStamp + 1; |
| |
| newState.myReadingProblems = readerResult.readingProblems; |
| newState.myLocalRepository = settings.getEffectiveLocalRepository(); |
| |
| newState.myActivatedProfilesIds = readerResult.activatedProfiles; |
| |
| MavenModel model = readerResult.mavenModel; |
| |
| newState.myMavenId = model.getMavenId(); |
| if (model.getParent() != null) { |
| newState.myParentId = model.getParent().getMavenId(); |
| } |
| |
| newState.myPackaging = model.getPackaging(); |
| newState.myName = model.getName(); |
| |
| newState.myFinalName = model.getBuild().getFinalName(); |
| newState.myDefaultGoal = model.getBuild().getDefaultGoal(); |
| |
| newState.myBuildDirectory = model.getBuild().getDirectory(); |
| newState.myOutputDirectory = model.getBuild().getOutputDirectory(); |
| newState.myTestOutputDirectory = model.getBuild().getTestOutputDirectory(); |
| |
| doSetFolders(newState, readerResult); |
| |
| newState.myFilters = model.getBuild().getFilters(); |
| newState.myProperties = model.getProperties(); |
| |
| doSetResolvedAttributes(newState, readerResult, resetArtifacts); |
| |
| MavenModelPropertiesPatcher.patch(newState.myProperties, newState.myPlugins); |
| |
| newState.myModulesPathsAndNames = collectModulePathsAndNames(model, getDirectory()); |
| Collection<String> newProfiles = collectProfilesIds(model.getProfiles()); |
| if (resetProfiles || newState.myProfilesIds == null) { |
| newState.myProfilesIds = newProfiles; |
| } |
| else { |
| Set<String> mergedProfiles = new THashSet<String>(newState.myProfilesIds); |
| mergedProfiles.addAll(newProfiles); |
| newState.myProfilesIds = new ArrayList<String>(mergedProfiles); |
| } |
| |
| newState.myModelMap = readerResult.nativeModelMap; |
| |
| return setState(newState); |
| } |
| |
| private MavenProjectChanges setState(State newState) { |
| MavenProjectChanges changes = myState.getChanges(newState); |
| myState = newState; |
| return changes; |
| } |
| |
| private static void doSetResolvedAttributes(State state, |
| MavenProjectReaderResult readerResult, |
| boolean reset) { |
| MavenModel model = readerResult.mavenModel; |
| |
| Set<MavenId> newUnresolvedArtifacts = new THashSet<MavenId>(); |
| LinkedHashSet<MavenRemoteRepository> newRepositories = new LinkedHashSet<MavenRemoteRepository>(); |
| LinkedHashSet<MavenArtifact> newDependencies = new LinkedHashSet<MavenArtifact>(); |
| LinkedHashSet<MavenArtifactNode> newDependencyTree = new LinkedHashSet<MavenArtifactNode>(); |
| LinkedHashSet<MavenPlugin> newPlugins = new LinkedHashSet<MavenPlugin>(); |
| LinkedHashSet<MavenArtifact> newExtensions = new LinkedHashSet<MavenArtifact>(); |
| |
| if (!reset) { |
| if (state.myUnresolvedArtifactIds != null) newUnresolvedArtifacts.addAll(state.myUnresolvedArtifactIds); |
| if (state.myRemoteRepositories != null) newRepositories.addAll(state.myRemoteRepositories); |
| if (state.myDependencies != null) newDependencies.addAll(state.myDependencies); |
| if (state.myDependencyTree != null) newDependencyTree.addAll(state.myDependencyTree); |
| if (state.myPlugins != null) newPlugins.addAll(state.myPlugins); |
| if (state.myExtensions != null) newExtensions.addAll(state.myExtensions); |
| } |
| |
| newUnresolvedArtifacts.addAll(readerResult.unresolvedArtifactIds); |
| newRepositories.addAll(model.getRemoteRepositories()); |
| newDependencyTree.addAll(model.getDependencyTree()); |
| newDependencies.addAll(model.getDependencies()); |
| newPlugins.addAll(model.getPlugins()); |
| newExtensions.addAll(model.getExtensions()); |
| |
| state.myUnresolvedArtifactIds = newUnresolvedArtifacts; |
| state.myRemoteRepositories = new ArrayList<MavenRemoteRepository>(newRepositories); |
| state.myDependencies = new ArrayList<MavenArtifact>(newDependencies); |
| state.myDependencyTree = new ArrayList<MavenArtifactNode>(newDependencyTree); |
| state.myPlugins = new ArrayList<MavenPlugin>(newPlugins); |
| state.myExtensions = new ArrayList<MavenArtifact>(newExtensions); |
| } |
| |
| private MavenProjectChanges setFolders(MavenProjectReaderResult readerResult) { |
| State newState = myState.clone(); |
| doSetFolders(newState, readerResult); |
| return setState(newState); |
| } |
| |
| private static void doSetFolders(State newState, MavenProjectReaderResult readerResult) { |
| MavenModel model = readerResult.mavenModel; |
| newState.mySources = model.getBuild().getSources(); |
| newState.myTestSources = model.getBuild().getTestSources(); |
| |
| newState.myResources = model.getBuild().getResources(); |
| newState.myTestResources = model.getBuild().getTestResources(); |
| } |
| |
| private static Map<String, String> collectModulePathsAndNames(MavenModel mavenModel, String baseDir) { |
| String basePath = baseDir + "/"; |
| Map<String, String> result = new LinkedHashMap<String, String>(); |
| for (Map.Entry<String, String> each : collectModulesRelativePathsAndNames(mavenModel).entrySet()) { |
| result.put(new Path(basePath + each.getKey()).getPath(), each.getValue()); |
| } |
| return result; |
| } |
| |
| private static Map<String, String> collectModulesRelativePathsAndNames(MavenModel mavenModel) { |
| LinkedHashMap<String, String> result = new LinkedHashMap<String, String>(); |
| for (String name : mavenModel.getModules()) { |
| name = name.trim(); |
| |
| if (name.length() == 0) continue; |
| |
| String originalName = name; |
| // module name can be relative and contain either / of \\ separators |
| |
| name = FileUtil.toSystemIndependentName(name); |
| if (!name.endsWith("/")) name += "/"; |
| name += MavenConstants.POM_XML; |
| |
| result.put(name, originalName); |
| } |
| return result; |
| } |
| |
| private static Collection<String> collectProfilesIds(Collection<MavenProfile> profiles) { |
| if (profiles == null) return Collections.emptyList(); |
| |
| Set<String> result = new THashSet<String>(profiles.size()); |
| for (MavenProfile each : profiles) { |
| result.add(each.getId()); |
| } |
| return result; |
| } |
| |
| public long getLastReadStamp() { |
| return myState.myLastReadStamp; |
| } |
| |
| @NotNull |
| public VirtualFile getFile() { |
| return myFile; |
| } |
| |
| @NotNull |
| public String getPath() { |
| return myFile.getPath(); |
| } |
| |
| @NotNull |
| public String getDirectory() { |
| return myFile.getParent().getPath(); |
| } |
| |
| @NotNull |
| public VirtualFile getDirectoryFile() { |
| return myFile.getParent(); |
| } |
| |
| @Nullable |
| public VirtualFile getProfilesXmlFile() { |
| return MavenUtil.findProfilesXmlFile(myFile); |
| } |
| |
| @NotNull |
| public File getProfilesXmlIoFile() { |
| return MavenUtil.getProfilesXmlIoFile(myFile); |
| } |
| |
| public boolean hasReadingProblems() { |
| return !myState.myReadingProblems.isEmpty(); |
| } |
| |
| @Nullable |
| public String getName() { |
| return myState.myName; |
| } |
| |
| @NotNull |
| public String getDisplayName() { |
| State state = myState; |
| if (StringUtil.isEmptyOrSpaces(state.myName)) return state.myMavenId.getArtifactId(); |
| return state.myName; |
| } |
| |
| @NotNull |
| public Map<String, String> getModelMap() { |
| return myState.myModelMap; |
| } |
| |
| @NotNull |
| public MavenId getMavenId() { |
| return myState.myMavenId; |
| } |
| |
| @Nullable |
| public MavenId getParentId() { |
| return myState.myParentId; |
| } |
| |
| @NotNull |
| public String getPackaging() { |
| return myState.myPackaging; |
| } |
| |
| @NotNull |
| public String getFinalName() { |
| return myState.myFinalName; |
| } |
| |
| @Nullable |
| public String getDefaultGoal() { |
| return myState.myDefaultGoal; |
| } |
| |
| @NotNull |
| public String getBuildDirectory() { |
| return myState.myBuildDirectory; |
| } |
| |
| @NotNull |
| public String getGeneratedSourcesDirectory(boolean testSources) { |
| return getBuildDirectory() + (testSources ? "/generated-test-sources" : "/generated-sources"); |
| } |
| |
| @NotNull |
| public String getAnnotationProcessorDirectory(boolean testSources) { |
| Element cfg = getPluginGoalConfiguration("org.bsc.maven", "maven-processor-plugin", testSources ? "process-test" : "process"); |
| if (cfg != null) { |
| String out = MavenJDOMUtil.findChildValueByPath(cfg, "outputDirectory"); |
| if (out == null) { |
| out = MavenJDOMUtil.findChildValueByPath(cfg, "defaultOutputDirectory"); |
| if (out == null) { |
| return getBuildDirectory() + "/generated-sources/apt"; |
| } |
| } |
| |
| if (!new File(out).isAbsolute()) { |
| out = getDirectory() + '/' + out; |
| } |
| |
| return out; |
| } |
| |
| String def = getGeneratedSourcesDirectory(testSources) + (testSources ? "/test-annotations" : "/annotations"); |
| return MavenJDOMUtil.findChildValueByPath(getCompilerConfig(), |
| testSources ? "generatedTestSourcesDirectory" : "generatedSourcesDirectory", |
| def); |
| } |
| |
| @NotNull |
| public ProcMode getProcMode() { |
| Element compilerConfiguration = getPluginExecutionConfiguration("org.apache.maven.plugins", "maven-compiler-plugin", "default-compile"); |
| if (compilerConfiguration == null) { |
| compilerConfiguration = getCompilerConfig(); |
| } |
| |
| if (compilerConfiguration == null) { |
| return ProcMode.BOTH; |
| } |
| |
| Element procElement = compilerConfiguration.getChild("proc"); |
| if (procElement != null) { |
| String procMode = procElement.getValue(); |
| return ("only".equalsIgnoreCase(procMode)) ? ProcMode.ONLY : ("none".equalsIgnoreCase(procMode)) ? ProcMode.NONE : ProcMode.BOTH; |
| } |
| |
| String compilerArgument = compilerConfiguration.getChildTextTrim("compilerArgument"); |
| if ("-proc:none".equals(compilerArgument)) { |
| return ProcMode.NONE; |
| } |
| if ("-proc:only".equals(compilerArgument)) { |
| return ProcMode.ONLY; |
| } |
| |
| //Element compilerArguments = compilerConfiguration.getChild("compilerArguments"); |
| //if (compilerArguments != null) { |
| // for (Element element : (List<Element>)compilerArguments.getChildren()) { |
| // String argName = element.getName(); |
| // if (argName.startsWith("-")) { |
| // argName = argName.substring(1); |
| // } |
| // |
| // if ("proc:none".equals(argName)) { |
| // return ProcMode.NONE; |
| // } |
| // if ("proc:only".equals(argName)) { |
| // return ProcMode.ONLY; |
| // } |
| // } |
| //} |
| |
| return ProcMode.BOTH; |
| } |
| |
| //@Nullable |
| //private static Element getAnnotationProcessorsConfiguration(@Nullable Element compilerConfig) { |
| // return (compilerConfig == null) ? null : compilerConfig.getChild("annotationProcessors"); |
| //} |
| |
| public Map<String, String> getAnnotationProcessorOptions() { |
| Element compilerConfig = getCompilerConfig(); |
| |
| if (compilerConfig == null) return Collections.emptyMap(); |
| |
| Map<String, String> res = null; |
| |
| String compilerArgument = compilerConfig.getChildText("compilerArgument"); |
| if (!StringUtil.isEmptyOrSpaces(compilerArgument)) { |
| ParametersList parametersList = new ParametersList(); |
| parametersList.addParametersString(compilerArgument); |
| |
| for (String param : parametersList.getParameters()) { |
| if (param.startsWith("-A")) { |
| int idx = param.indexOf('=', 3); |
| if (idx >= 0) { |
| if (res == null) { |
| res = new LinkedHashMap<String, String>(); |
| } |
| |
| res.put(param.substring(2, idx), param.substring(idx + 1)); |
| } |
| } |
| } |
| } |
| |
| Element compilerArguments = compilerConfig.getChild("compilerArguments"); |
| if (compilerArguments != null) { |
| for (Element e : compilerArguments.getChildren()){ |
| String name = e.getName(); |
| if (name.startsWith("-")) { |
| name = name.substring(1); |
| } |
| |
| if (name.length() > 1 && name.charAt(0) == 'A') { |
| if (res == null) { |
| res = new LinkedHashMap<String, String>(); |
| } |
| |
| res.put(name.substring(1), e.getTextTrim()); |
| } |
| } |
| } |
| |
| if (res == null) return Collections.emptyMap(); |
| |
| return res; |
| } |
| |
| @Nullable |
| public List<String> getDeclaredAnnotationProcessors() { |
| Element compilerConfig = getCompilerConfig(); |
| if (compilerConfig != null) { |
| Element processors = compilerConfig.getChild("annotationProcessors"); |
| if (processors != null) { |
| List<String> res = new ArrayList<String>(); |
| |
| for (Element element : processors.getChildren("annotationProcessor")){ |
| String processorClassName = element.getTextTrim(); |
| if (!processorClassName.isEmpty()) { |
| res.add(processorClassName); |
| } |
| } |
| |
| return res; |
| } |
| } |
| |
| MavenPlugin bscMavenPlugin = findPlugin("org.bsc.maven", "maven-processor-plugin"); |
| if (bscMavenPlugin != null) { |
| Element cfg = bscMavenPlugin.getGoalConfiguration("process"); |
| if (cfg == null) { |
| cfg = bscMavenPlugin.getConfigurationElement(); |
| } |
| |
| if (cfg != null) { |
| Element processors = cfg.getChild("processors"); |
| if (processors != null) { |
| List<String> res = new ArrayList<String>(); |
| |
| for (Element element : processors.getChildren("processor")){ |
| String processorClassName = element.getTextTrim(); |
| if (!processorClassName.isEmpty()) { |
| res.add(processorClassName); |
| } |
| } |
| |
| return res; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| @NotNull |
| public String getOutputDirectory() { |
| return myState.myOutputDirectory; |
| } |
| |
| @NotNull |
| public String getTestOutputDirectory() { |
| return myState.myTestOutputDirectory; |
| } |
| |
| @NotNull |
| public List<String> getSources() { |
| return myState.mySources; |
| } |
| |
| @NotNull |
| public List<String> getTestSources() { |
| return myState.myTestSources; |
| } |
| |
| @NotNull |
| public List<MavenResource> getResources() { |
| return myState.myResources; |
| } |
| |
| @NotNull |
| public List<MavenResource> getTestResources() { |
| return myState.myTestResources; |
| } |
| |
| @NotNull |
| public List<String> getFilters() { |
| return myState.myFilters; |
| } |
| |
| public List<String> getFilterPropertiesFiles() { |
| List<String> res = getCachedValue(FILTERS_CACHE_KEY); |
| if (res == null) { |
| Element propCfg = getPluginGoalConfiguration("org.codehaus.mojo", "properties-maven-plugin", "read-project-properties"); |
| if (propCfg != null) { |
| Element files = propCfg.getChild("files"); |
| if (files != null) { |
| res = new ArrayList<String>(); |
| |
| for (Element file : files.getChildren("file")) { |
| File f = new File(file.getValue()); |
| if (!f.isAbsolute()) { |
| f = new File(getDirectory(), file.getValue()); |
| } |
| |
| res.add(f.getAbsolutePath()); |
| } |
| } |
| } |
| |
| if (res == null) { |
| res = getFilters(); |
| } |
| else { |
| res.addAll(getFilters()); |
| } |
| |
| res = putCachedValue(FILTERS_CACHE_KEY, res); |
| } |
| |
| return res; |
| } |
| |
| @NotNull |
| public MavenProjectChanges read(@NotNull MavenGeneralSettings generalSettings, |
| @NotNull MavenExplicitProfiles profiles, |
| @NotNull MavenProjectReader reader, |
| @NotNull MavenProjectReaderProjectLocator locator) { |
| return set(reader.readProject(generalSettings, myFile, profiles, locator), generalSettings, true, false, true); |
| } |
| |
| @NotNull |
| public Pair<MavenProjectChanges, NativeMavenProjectHolder> resolve(@NotNull Project project, |
| @NotNull MavenGeneralSettings generalSettings, |
| @NotNull MavenEmbedderWrapper embedder, |
| @NotNull MavenProjectReader reader, |
| @NotNull MavenProjectReaderProjectLocator locator, |
| @NotNull ResolveContext context) |
| throws MavenProcessCanceledException { |
| MavenProjectReaderResult result = reader.resolveProject(generalSettings, |
| embedder, |
| getFile(), |
| getActivatedProfilesIds(), |
| locator); |
| MavenProjectChanges changes = set(result, generalSettings, false, result.readingProblems.isEmpty(), false); |
| |
| if (result.nativeMavenProject != null) { |
| for (MavenImporter eachImporter : getSuitableImporters()) { |
| eachImporter.resolve(project, this, result.nativeMavenProject, embedder, context); |
| } |
| } |
| return Pair.create(changes, result.nativeMavenProject); |
| } |
| |
| @NotNull |
| public Pair<Boolean, MavenProjectChanges> resolveFolders(@NotNull MavenEmbedderWrapper embedder, |
| @NotNull MavenImportingSettings importingSettings, |
| @NotNull MavenConsole console) throws MavenProcessCanceledException { |
| MavenProjectReaderResult result = MavenProjectReader.generateSources(embedder, |
| importingSettings, |
| getFile(), |
| getActivatedProfilesIds(), |
| console); |
| if (result == null || !result.readingProblems.isEmpty()) return Pair.create(false, MavenProjectChanges.NONE); |
| MavenProjectChanges changes = setFolders(result); |
| return Pair.create(true, changes); |
| } |
| |
| public void resetCache() { |
| // todo a bit hacky |
| synchronized (myState) { |
| myState.resetCache(); |
| } |
| } |
| |
| public boolean isAggregator() { |
| return "pom".equals(getPackaging()) || !getModulePaths().isEmpty(); |
| } |
| |
| @NotNull |
| public List<MavenProjectProblem> getProblems() { |
| State state = myState; |
| synchronized (state) { |
| if (state.myProblemsCache == null) { |
| state.myProblemsCache = collectProblems(myFile, state); |
| } |
| return state.myProblemsCache; |
| } |
| } |
| |
| private static List<MavenProjectProblem> collectProblems(VirtualFile file, State state) { |
| List<MavenProjectProblem> result = new ArrayList<MavenProjectProblem>(); |
| |
| validateParent(file, state, result); |
| result.addAll(state.myReadingProblems); |
| |
| for (Map.Entry<String, String> each : state.myModulesPathsAndNames.entrySet()) { |
| if (LocalFileSystem.getInstance().findFileByPath(each.getKey()) == null) { |
| result.add(createDependencyProblem(file, ProjectBundle.message("maven.project.problem.moduleNotFound", each.getValue()))); |
| } |
| } |
| |
| validateDependencies(file, state, result); |
| validateExtensions(file, state, result); |
| validatePlugins(file, state, result); |
| |
| return result; |
| } |
| |
| private static void validateParent(VirtualFile file, State state, List<MavenProjectProblem> result) { |
| if (!isParentResolved(state)) { |
| result.add(createDependencyProblem(file, ProjectBundle.message("maven.project.problem.parentNotFound", state.myParentId))); |
| } |
| } |
| |
| private static void validateDependencies(VirtualFile file, State state, List<MavenProjectProblem> result) { |
| for (MavenArtifact each : getUnresolvedDependencies(state)) { |
| result.add(createDependencyProblem(file, ProjectBundle.message("maven.project.problem.unresolvedDependency", |
| each.getDisplayStringWithType()))); |
| } |
| } |
| |
| private static void validateExtensions(VirtualFile file, State state, List<MavenProjectProblem> result) { |
| for (MavenArtifact each : getUnresolvedExtensions(state)) { |
| result.add(createDependencyProblem(file, ProjectBundle.message("maven.project.problem.unresolvedExtension", |
| each.getDisplayStringSimple()))); |
| } |
| } |
| |
| private static void validatePlugins(VirtualFile file, State state, List<MavenProjectProblem> result) { |
| for (MavenPlugin each : getUnresolvedPlugins(state)) { |
| result.add(createDependencyProblem(file, ProjectBundle.message("maven.project.problem.unresolvedPlugin", each))); |
| } |
| } |
| |
| private static MavenProjectProblem createDependencyProblem(VirtualFile file, String description) { |
| return new MavenProjectProblem(file.getPath(), description, MavenProjectProblem.ProblemType.DEPENDENCY); |
| } |
| |
| private static boolean isParentResolved(State state) { |
| return !state.myUnresolvedArtifactIds.contains(state.myParentId); |
| } |
| |
| private static List<MavenArtifact> getUnresolvedDependencies(State state) { |
| synchronized (state) { |
| if (state.myUnresolvedDependenciesCache == null) { |
| List<MavenArtifact> result = new ArrayList<MavenArtifact>(); |
| for (MavenArtifact each : state.myDependencies) { |
| if (!each.isResolved()) result.add(each); |
| } |
| state.myUnresolvedDependenciesCache = result; |
| } |
| return state.myUnresolvedDependenciesCache; |
| } |
| } |
| |
| private static List<MavenArtifact> getUnresolvedExtensions(State state) { |
| synchronized (state) { |
| if (state.myUnresolvedExtensionsCache == null) { |
| List<MavenArtifact> result = new ArrayList<MavenArtifact>(); |
| for (MavenArtifact each : state.myExtensions) { |
| // Collect only extensions that were attempted to be resolved. |
| // It is because embedder does not even try to resolve extensions that |
| // are not necessary. |
| if (state.myUnresolvedArtifactIds.contains(each.getMavenId()) |
| && !pomFileExists(state.myLocalRepository, each)) { |
| result.add(each); |
| } |
| } |
| state.myUnresolvedExtensionsCache = result; |
| } |
| return state.myUnresolvedExtensionsCache; |
| } |
| } |
| |
| private static boolean pomFileExists(File localRepository, MavenArtifact artifact) { |
| return MavenArtifactUtil.hasArtifactFile(localRepository, artifact.getMavenId(), "pom"); |
| } |
| |
| private static List<MavenPlugin> getUnresolvedPlugins(State state) { |
| synchronized (state) { |
| if (state.myUnresolvedPluginsCache == null) { |
| List<MavenPlugin> result = new ArrayList<MavenPlugin>(); |
| for (MavenPlugin each : getDeclaredPlugins(state)) { |
| if (!MavenArtifactUtil.hasArtifactFile(state.myLocalRepository, each.getMavenId())) { |
| result.add(each); |
| } |
| } |
| state.myUnresolvedPluginsCache = result; |
| } |
| return state.myUnresolvedPluginsCache; |
| } |
| } |
| |
| @NotNull |
| public List<VirtualFile> getExistingModuleFiles() { |
| LocalFileSystem fs = LocalFileSystem.getInstance(); |
| |
| List<VirtualFile> result = new ArrayList<VirtualFile>(); |
| Set<String> pathsInStack = getModulePaths(); |
| for (String each : pathsInStack) { |
| VirtualFile f = fs.findFileByPath(each); |
| if (f != null) result.add(f); |
| } |
| return result; |
| } |
| |
| @NotNull |
| public Set<String> getModulePaths() { |
| return getModulesPathsAndNames().keySet(); |
| } |
| |
| @NotNull |
| public Map<String, String> getModulesPathsAndNames() { |
| return myState.myModulesPathsAndNames; |
| } |
| |
| @NotNull |
| public Collection<String> getProfilesIds() { |
| return myState.myProfilesIds; |
| } |
| |
| @NotNull |
| public MavenExplicitProfiles getActivatedProfilesIds() { |
| return myState.myActivatedProfilesIds; |
| } |
| |
| @NotNull |
| public List<MavenArtifact> getDependencies() { |
| return myState.myDependencies; |
| } |
| |
| @NotNull |
| public List<MavenArtifactNode> getDependencyTree() { |
| return myState.myDependencyTree; |
| } |
| |
| @NotNull |
| public Set<String> getSupportedPackagings() { |
| Set<String> result = ContainerUtil.newHashSet(MavenConstants.TYPE_POM, |
| MavenConstants.TYPE_JAR, |
| "ejb", "ejb-client", "war", "ear", "bundle", "maven-plugin"); |
| for (MavenImporter each : getSuitableImporters()) { |
| each.getSupportedPackagings(result); |
| } |
| return result; |
| } |
| |
| public Set<String> getDependencyTypesFromImporters(@NotNull SupportedRequestType type) { |
| THashSet<String> res = new THashSet<String>(); |
| |
| for (MavenImporter each : getSuitableImporters()) { |
| each.getSupportedDependencyTypes(res, type); |
| } |
| |
| return res; |
| } |
| |
| @NotNull |
| public Set<String> getSupportedDependencyScopes() { |
| Set<String> result = new THashSet<String>(Arrays.asList(MavenConstants.SCOPE_COMPILE, |
| MavenConstants.SCOPE_PROVIDED, |
| MavenConstants.SCOPE_RUNTIME, |
| MavenConstants.SCOPE_TEST, |
| MavenConstants.SCOPE_SYSTEM)); |
| for (MavenImporter each : getSuitableImporters()) { |
| each.getSupportedDependencyScopes(result); |
| } |
| return result; |
| } |
| |
| public void addDependency(@NotNull MavenArtifact dependency) { |
| State state = myState; |
| List<MavenArtifact> dependenciesCopy = new ArrayList<MavenArtifact>(state.myDependencies); |
| dependenciesCopy.add(dependency); |
| state.myDependencies = dependenciesCopy; |
| |
| state.myCache.clear(); |
| } |
| |
| @NotNull |
| public List<MavenArtifact> findDependencies(@NotNull MavenProject depProject) { |
| return findDependencies(depProject.getMavenId()); |
| } |
| |
| public List<MavenArtifact> findDependencies(@NotNull MavenId id) { |
| return getDependencyArtifactIndex().findArtifacts(id); |
| } |
| |
| @NotNull |
| public List<MavenArtifact> findDependencies(@Nullable String groupId, @Nullable String artifactId) { |
| return getDependencyArtifactIndex().findArtifacts(groupId, artifactId); |
| } |
| |
| public boolean hasUnresolvedArtifacts() { |
| State state = myState; |
| return !isParentResolved(state) |
| || !getUnresolvedDependencies(state).isEmpty() |
| || !getUnresolvedExtensions(state).isEmpty(); |
| } |
| |
| public boolean hasUnresolvedPlugins() { |
| return !getUnresolvedPlugins(myState).isEmpty(); |
| } |
| |
| @NotNull |
| public List<MavenPlugin> getPlugins() { |
| return myState.myPlugins; |
| } |
| |
| @NotNull |
| public List<MavenPlugin> getDeclaredPlugins() { |
| return getDeclaredPlugins(myState); |
| } |
| |
| private static List<MavenPlugin> getDeclaredPlugins(State state) { |
| return ContainerUtil.findAll(state.myPlugins, new Condition<MavenPlugin>() { |
| public boolean value(MavenPlugin mavenPlugin) { |
| return !mavenPlugin.isDefault(); |
| } |
| }); |
| } |
| |
| @Nullable |
| public Element getPluginConfiguration(@Nullable String groupId, @Nullable String artifactId) { |
| return getPluginGoalConfiguration(groupId, artifactId, null); |
| } |
| |
| @Nullable |
| public Element getPluginGoalConfiguration(@Nullable String groupId, @Nullable String artifactId, @Nullable String goal) { |
| MavenPlugin plugin = findPlugin(groupId, artifactId); |
| if (plugin == null) return null; |
| |
| if (goal == null) { |
| return plugin.getConfigurationElement(); |
| } |
| |
| return plugin.getGoalConfiguration(goal); |
| } |
| |
| public Element getPluginExecutionConfiguration(@Nullable String groupId, @Nullable String artifactId, @NotNull String executionId) { |
| MavenPlugin plugin = findPlugin(groupId, artifactId); |
| if (plugin == null) return null; |
| |
| return plugin.getExecutionConfiguration(executionId); |
| } |
| |
| @Nullable |
| public MavenPlugin findPlugin(@Nullable String groupId, @Nullable String artifactId) { |
| return findPlugin(groupId, artifactId, false); |
| } |
| |
| @Nullable |
| public MavenPlugin findPlugin(@Nullable String groupId, @Nullable String artifactId, final boolean explicitlyDeclaredOnly) { |
| final List<MavenPlugin> plugins = explicitlyDeclaredOnly ? getDeclaredPlugins() : getPlugins(); |
| for (MavenPlugin each : plugins) { |
| if (each.getMavenId().equals(groupId, artifactId)) return each; |
| } |
| return null; |
| } |
| |
| @Nullable |
| public String getEncoding() { |
| String encoding = myState.myProperties.getProperty("project.build.sourceEncoding"); |
| if (encoding != null) return encoding; |
| |
| Element pluginConfiguration = getPluginConfiguration("org.apache.maven.plugins", "maven-resources-plugin"); |
| if (pluginConfiguration != null) { |
| return pluginConfiguration.getChildTextTrim("encoding"); |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public String getSourceLevel() { |
| return getCompilerLevel("source"); |
| } |
| |
| @Nullable |
| public String getTargetLevel() { |
| return getCompilerLevel("target"); |
| } |
| |
| @Nullable |
| private String getCompilerLevel(String level) { |
| String result = MavenJDOMUtil.findChildValueByPath(getCompilerConfig(), level); |
| |
| if (result == null) { |
| result = myState.myProperties.getProperty("maven.compiler." + level); |
| } |
| |
| return normalizeCompilerLevel(result); |
| } |
| |
| @Nullable |
| private Element getCompilerConfig() { |
| return getPluginConfiguration("org.apache.maven.plugins", "maven-compiler-plugin"); |
| } |
| |
| @Nullable |
| public static String normalizeCompilerLevel(@Nullable String level) { |
| return COMPILER_LEVEL_TABLE.get(level); |
| } |
| |
| @NotNull |
| public Properties getProperties() { |
| return myState.myProperties; |
| } |
| |
| @NotNull |
| public File getLocalRepository() { |
| return myState.myLocalRepository; |
| } |
| |
| @NotNull |
| public List<MavenRemoteRepository> getRemoteRepositories() { |
| return myState.myRemoteRepositories; |
| } |
| |
| @NotNull |
| public List<MavenImporter> getSuitableImporters() { |
| return MavenImporter.getSuitableImporters(this); |
| } |
| |
| @NotNull |
| public ModuleType getModuleType() { |
| final List<MavenImporter> importers = getSuitableImporters(); |
| // getSuitableImporters() guarantees that all returned importers require the same module type |
| return importers.size() > 0 ? importers.get(0).getModuleType() : StdModuleTypes.JAVA; |
| } |
| |
| @NotNull |
| public Pair<String, String> getClassifierAndExtension(@NotNull MavenArtifact artifact, @NotNull MavenExtraArtifactType type) { |
| for (MavenImporter each : getSuitableImporters()) { |
| Pair<String, String> result = each.getExtraArtifactClassifierAndExtension(artifact, type); |
| if (result != null) return result; |
| } |
| return Pair.create(type.getDefaultClassifier(), type.getDefaultExtension()); |
| } |
| |
| public MavenArtifactIndex getDependencyArtifactIndex() { |
| MavenArtifactIndex res = getCachedValue(DEPENDENCIES_CACHE_KEY); |
| if (res == null) { |
| res = MavenArtifactIndex.build(getDependencies()); |
| res = putCachedValue(DEPENDENCIES_CACHE_KEY, res); |
| } |
| |
| return res; |
| } |
| |
| @Nullable |
| public <V> V getCachedValue(Key<V> key) { |
| //noinspection unchecked |
| return (V)myState.myCache.get(key); |
| } |
| |
| @NotNull |
| public <V> V putCachedValue(Key<V> key, @NotNull V value) { |
| ConcurrentHashMap<Key, Object> map = myState.myCache; |
| Object oldValue = map.putIfAbsent(key, value); |
| if (oldValue != null) { |
| return (V)oldValue; |
| } |
| |
| return value; |
| } |
| |
| @Override |
| public String toString() { |
| return getMavenId().toString(); |
| } |
| |
| private static class State implements Cloneable, Serializable { |
| long myLastReadStamp = 0; |
| |
| MavenId myMavenId; |
| MavenId myParentId; |
| String myPackaging; |
| String myName; |
| |
| String myFinalName; |
| String myDefaultGoal; |
| |
| String myBuildDirectory; |
| String myOutputDirectory; |
| String myTestOutputDirectory; |
| |
| List<String> mySources; |
| List<String> myTestSources; |
| List<MavenResource> myResources; |
| List<MavenResource> myTestResources; |
| |
| List<String> myFilters; |
| Properties myProperties; |
| List<MavenPlugin> myPlugins; |
| List<MavenArtifact> myExtensions; |
| |
| List<MavenArtifact> myDependencies; |
| List<MavenArtifactNode> myDependencyTree; |
| List<MavenRemoteRepository> myRemoteRepositories; |
| |
| Map<String, String> myModulesPathsAndNames; |
| |
| Map<String, String> myModelMap; |
| |
| Collection<String> myProfilesIds; |
| MavenExplicitProfiles myActivatedProfilesIds; |
| |
| Collection<MavenProjectProblem> myReadingProblems; |
| Set<MavenId> myUnresolvedArtifactIds; |
| File myLocalRepository; |
| |
| volatile List<MavenProjectProblem> myProblemsCache; |
| volatile List<MavenArtifact> myUnresolvedDependenciesCache; |
| volatile List<MavenPlugin> myUnresolvedPluginsCache; |
| volatile List<MavenArtifact> myUnresolvedExtensionsCache; |
| |
| transient ConcurrentHashMap<Key, Object> myCache = new ConcurrentHashMap<Key, Object>(); |
| |
| @Override |
| public State clone() { |
| try { |
| State result = (State)super.clone(); |
| myCache = new ConcurrentHashMap<Key, Object>(); |
| result.resetCache(); |
| return result; |
| } |
| catch (CloneNotSupportedException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private void resetCache() { |
| myProblemsCache = null; |
| myUnresolvedDependenciesCache = null; |
| myUnresolvedPluginsCache = null; |
| myUnresolvedExtensionsCache = null; |
| |
| myCache.clear(); |
| } |
| |
| public MavenProjectChanges getChanges(State other) { |
| if (myLastReadStamp == 0) return MavenProjectChanges.ALL; |
| |
| MavenProjectChanges result = new MavenProjectChanges(); |
| |
| result.packaging = !Comparing.equal(myPackaging, other.myPackaging); |
| |
| result.output = !Comparing.equal(myFinalName, other.myFinalName) |
| || !Comparing.equal(myBuildDirectory, other.myBuildDirectory) |
| || !Comparing.equal(myOutputDirectory, other.myOutputDirectory) |
| || !Comparing.equal(myTestOutputDirectory, other.myTestOutputDirectory); |
| |
| result.sources = !Comparing.equal(mySources, other.mySources) |
| || !Comparing.equal(myTestSources, other.myTestSources) |
| || !Comparing.equal(myResources, other.myResources) |
| || !Comparing.equal(myTestResources, other.myTestResources); |
| |
| boolean repositoryChanged = !Comparing.equal(myLocalRepository, other.myLocalRepository); |
| |
| result.dependencies = repositoryChanged || !Comparing.equal(myDependencies, other.myDependencies); |
| |
| result.plugins = repositoryChanged || !Comparing.equal(myPlugins, other.myPlugins); |
| |
| return result; |
| } |
| |
| private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { |
| in.defaultReadObject(); |
| myCache = new ConcurrentHashMap<Key, Object>(); |
| } |
| } |
| } |