/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.maven.server.core.project;
import org.eclipse.che.commons.lang.PathUtil;
import org.eclipse.che.maven.data.MavenArtifact;
import org.eclipse.che.maven.data.MavenConstants;
import org.eclipse.che.maven.data.MavenKey;
import org.eclipse.che.maven.data.MavenModel;
import org.eclipse.che.maven.data.MavenPlugin;
import org.eclipse.che.maven.data.MavenProblemType;
import org.eclipse.che.maven.data.MavenProjectProblem;
import org.eclipse.che.maven.data.MavenRemoteRepository;
import org.eclipse.che.maven.data.MavenResource;
import org.eclipse.che.plugin.maven.server.MavenServerManager;
import org.eclipse.che.plugin.maven.server.MavenServerWrapper;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.jdom.Element;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Evgen Vidolob
*/
public class MavenProject {
private final IProject project;
private final IWorkspace workspace;
private volatile Info info = new Info();
public MavenProject(IProject project, IWorkspace workspace) {
this.project = project;
this.workspace = workspace;
}
public MavenKey getParentKey() {
return info.parentKey;
}
public MavenKey getMavenKey() {
return info.mavenKey;
}
public String getPackaging() {
return info.packaging;
}
public Properties getProperties() {
return info.properties;
}
public List<String> getSources() {
return info.sources;
}
public List<MavenResource> getResources() {
return info.resources;
}
public List<String> getTestSources() {
return info.testSources;
}
public List<MavenResource> getTestResources() {
return info.testResources;
}
public String getName() {
String name = info.name;
if (name == null) {
name = info.mavenKey.getArtifactId();
}
return name;
}
public String getSourceLevel() {
//todo
throw new UnsupportedOperationException();
}
public String getTargetLevel() {
//todo
throw new UnsupportedOperationException();
}
public List<String> getModules() {
return new ArrayList<>(info.modulesNameToPath.keySet());
}
public List<MavenPlugin> getPlugins() {
return info.plugins;
}
public List<MavenProjectProblem> getProblems() {
synchronized (info) {
if (info.problemsCache == null) {
info.problemsCache = generateProblems();
}
return info.problemsCache;
}
}
private List<MavenProjectProblem> generateProblems() {
List<MavenProjectProblem> result = new ArrayList<>();
result.addAll(info.problems);
result.addAll(info.modulesNameToPath.entrySet().stream().filter(entry -> !project.getFolder(entry.getKey()).exists())
.map(entry -> new MavenProjectProblem(getPomPath(),
"Can't find module: " + entry.getKey(),
MavenProblemType.DEPENDENCY))
.collect(Collectors.toList()));
result.addAll(getDependencies().stream().filter(artifact -> !artifact.isResolved())
.map(artifact -> new MavenProjectProblem(getPomPath(),
"Can't find dependency: " + artifact.getDisplayString(),
MavenProblemType.DEPENDENCY))
.collect(Collectors.toList()));
//TODO add unresolved plugins and extensions
return result;
}
private boolean isParentResolved() {
return info.unresolvedArtifacts.contains(info.parentKey);
}
public List<MavenArtifact> getDependencies() {
return info.dependencies;
}
/**
* Invoke maven to build project model.
*
* @param project to resolve
* @param mavenServer the maven server
* @return the modification types that applied to this project
*/
public MavenProjectModifications resolve(IProject project, MavenServerWrapper mavenServer, MavenServerManager serverManager) {
MavenModelReader reader = new MavenModelReader();
MavenModelReaderResult modelReaderResult =
reader.resolveMavenProject(getPom(project), mavenServer, info.activeProfiles, info.inactiveProfiles, serverManager);
return setModel(modelReaderResult, modelReaderResult.getProblems().isEmpty(), false);
}
public MavenProjectModifications read(MavenServerManager manager) {
return read(project, manager);
}
public MavenProjectModifications read(IProject project, MavenServerManager serverManager) {
MavenModelReader reader = new MavenModelReader();
return setModel(reader.readMavenProject(getPom(project), serverManager), false, true);
}
private MavenProjectModifications setModel(MavenModelReaderResult readerResult, boolean clearArtifacts, boolean clearProfiles) {
Info newInfo = info.clone();
newInfo.problems = readerResult.getProblems();
newInfo.activeProfiles = readerResult.getActiveProfiles();
MavenModel model = readerResult.getMavenModel();
newInfo.mavenKey = model.getMavenKey();
if (model.getParent() != null) {
newInfo.parentKey = model.getParent().getMavenKey();
}
newInfo.packaging = model.getPackaging();
newInfo.name = model.getName();
newInfo.sources = model.getBuild().getSources();
newInfo.testSources = model.getBuild().getTestSources();
newInfo.resources = model.getBuild().getResources();
newInfo.testResources = model.getBuild().getTestResources();
newInfo.properties = model.getProperties();
newInfo.filters = model.getBuild().getFilters();
Set<MavenRemoteRepository> remoteRepositories = new HashSet<>();
Set<MavenArtifact> extensions = new HashSet<>();
Set<MavenArtifact> dependencies = new HashSet<>();
Set<MavenPlugin> plugins = new HashSet<>();
Set<MavenKey> unresolvedArtifacts = new HashSet<>();
if (!clearArtifacts) {
if (info.remoteRepositories != null) {
remoteRepositories.addAll(info.remoteRepositories);
}
if (info.extensions != null) {
extensions.addAll(info.extensions);
}
if (info.dependencies != null) {
dependencies.addAll(info.dependencies);
}
if (info.plugins != null) {
plugins.addAll(info.plugins);
}
if (info.unresolvedArtifacts != null) {
unresolvedArtifacts.addAll(info.unresolvedArtifacts);
}
}
remoteRepositories.addAll(model.getRemoteRepositories());
extensions.addAll(model.getExtensions());
dependencies.addAll(model.getDependencies());
plugins.addAll(model.getPlugins());
unresolvedArtifacts.addAll(readerResult.getUnresolvedArtifacts());
newInfo.remoteRepositories = new ArrayList<>(remoteRepositories);
newInfo.extensions = new ArrayList<>(extensions);
newInfo.dependencies = new ArrayList<>(dependencies);
newInfo.plugins = new ArrayList<>(plugins);
newInfo.unresolvedArtifacts = unresolvedArtifacts;
newInfo.modulesNameToPath = collectModulesNameAndPath(model);
//TODO add profiles
return setInfo(newInfo);
}
private Map<String, String> collectModulesNameAndPath(MavenModel model) {
Map<String, String> result = new HashMap<>();
String projectPath = project.getFullPath().toOSString();
if (!projectPath.endsWith("/")) {
projectPath += "/";
}
for (String name : model.getModules()) {
result.put(name, PathUtil.toCanonicalPath(projectPath + name, false));
}
return result;
}
private MavenProjectModifications setInfo(Info newInfo) {
MavenProjectModifications modifications = info.generateChanges(newInfo);
info = newInfo;
info.problemsCache = null; //info has been changed, so we must to clean cache
return modifications;
}
private File getPom(IProject project) {
IFile file = project.getFile(MavenConstants.POM_FILE_NAME);
if (file == null) {
return null;
}
return file.getLocation().toFile();
}
/**
* @return workspace relative pom.xml path or null if pom.xml does not exist
*/
public String getPomPath() {
IFile file = project.getFile(MavenConstants.POM_FILE_NAME);
if (file == null) {
return null;
}
return file.getFullPath().toOSString();
}
public IProject getProject() {
return project;
}
public File getPomFile() {
return getPom(project);
}
public boolean containsAsModule(IPath modulePath) {
if (!project.getFullPath().equals(modulePath)) {
return false;
}
String moduleName = modulePath.lastSegment();
List<String> modules = getModules();
return modules.contains(moduleName);
}
public List<IProject> getModulesProjects() {
Collection<String> modulesPath = info.modulesNameToPath.values();
return modulesPath.stream().map(path -> {
if (path.endsWith(MavenConstants.POM_FILE_NAME)) {
return workspace.getRoot().getProject(new Path(path).removeLastSegments(1).toOSString());
}
return workspace.getRoot().getProject(path);
}).collect(Collectors.toList());
}
public Collection<String> getModulesPath() {
return info.modulesNameToPath.values();
}
public Element getPluginConfiguration(String groupId, String artifactId, String goal) {
MavenPlugin plugin = findPlugin(groupId, artifactId);
if (plugin == null) {
return null;
}
if (goal == null) {
return plugin.getConfiguration();
} else {
return plugin.getGoalConfiguration(goal);
}
}
public MavenPlugin findPlugin(String groupId, String artifactId) {
return findPlugin(groupId, artifactId, false);
}
private MavenPlugin findPlugin(String groupId, String artifactId, boolean declaredOnly) {
List<MavenPlugin> plugins;
if (declaredOnly) {
plugins = getDeclaredPlugins();
} else {
plugins = getPlugins();
}
for (MavenPlugin plugin : plugins) {
if (plugin.getGroupId().equals(groupId) && plugin.getArtifactId().equals(artifactId)) {
return plugin;
}
}
return null;
}
private List<MavenPlugin> getDeclaredPlugins() {
return info.plugins.stream().filter(plugin -> !plugin.isDefault()).collect(Collectors.toList());
}
private static class Info implements Cloneable {
public MavenKey mavenKey;
public MavenKey parentKey;
public String packaging;
public String name;
public Properties properties;
public List<String> sources;
public List<String> testSources;
public List<MavenResource> resources;
public List<MavenResource> testResources;
public List<String> activeProfiles;
public List<String> inactiveProfiles;
public List<String> filters;
public List<MavenArtifact> dependencies;
public List<MavenArtifact> extensions;
public List<MavenPlugin> plugins;
public List<MavenProjectProblem> problems;
public List<MavenRemoteRepository> remoteRepositories;
public Map<String, String> modulesNameToPath;
public Set<MavenKey> unresolvedArtifacts;
public List<MavenProjectProblem> problemsCache;
public Info clone() {
try {
Info newInfo = (Info) super.clone();
return newInfo;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public MavenProjectModifications generateChanges(Info newInfo) {
MavenProjectModifications result = new MavenProjectModifications();
result.setPackaging(!Objects.equals(packaging, newInfo.packaging));
result.setSources(!Objects.equals(sources, newInfo.sources)
|| !Objects.equals(resources, newInfo.resources)
|| !Objects.equals(testSources, newInfo.testSources)
|| !Objects.equals(testResources, newInfo.testResources));
result.setDependencies(!Objects.equals(dependencies, newInfo.dependencies));
result.setPlugins(!Objects.equals(plugins, newInfo.plugins));
return result;
}
}
}