/*******************************************************************************
* Copyright (c) 2008-2010 Sonatype, Inc.
* 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:
* Sonatype, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.m2e.core.project.configurator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.ConfigurationContainer;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.embedder.IMaven;
import org.eclipse.m2e.core.embedder.IMavenConfiguration;
import org.eclipse.m2e.core.embedder.IMavenExecutionContext;
import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.internal.Messages;
import org.eclipse.m2e.core.internal.lifecyclemapping.LifecycleMappingFactory;
import org.eclipse.m2e.core.internal.markers.IMavenMarkerManager;
import org.eclipse.m2e.core.lifecyclemapping.model.IPluginExecutionMetadata;
import org.eclipse.m2e.core.lifecyclemapping.model.PluginExecutionAction;
import org.eclipse.m2e.core.project.IMavenProjectChangedListener;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.IMavenProjectRegistry;
import org.eclipse.m2e.core.project.MavenProjectChangedEvent;
/**
* Used to configure maven projects.
*
* @author Igor Fedorenko
*/
public abstract class AbstractProjectConfigurator implements IExecutableExtension, IMavenProjectChangedListener {
private static final Logger log = LoggerFactory.getLogger(AbstractProjectConfigurator.class);
public static final String ATTR_ID = "id"; //$NON-NLS-1$
public static final String ATTR_NAME = "name"; //$NON-NLS-1$
public static final String ATTR_CLASS = "class"; //$NON-NLS-1$
private String id;
private String name;
protected IMavenProjectRegistry projectManager;
protected IMavenConfiguration mavenConfiguration;
protected IMavenMarkerManager markerManager;
protected IMaven maven = MavenPlugin.getMaven();
public void setProjectManager(IMavenProjectRegistry projectManager) {
this.projectManager = projectManager;
}
public void setMavenConfiguration(IMavenConfiguration mavenConfiguration) {
this.mavenConfiguration = mavenConfiguration;
}
public void setMarkerManager(IMavenMarkerManager markerManager) {
this.markerManager = markerManager;
}
/**
* Configures Eclipse project passed in ProjectConfigurationRequest, using information from Maven project and other
* configuration request parameters
* <p>
* <i>Should be implemented by subclass</i>
*
* @param request a project configuration request
* @param monitor a progress monitor
*/
public abstract void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException;
/**
* Removes Maven specific configuration from the project passed in ProjectConfigurationRequest
*
* @param request a project un-configuration request
* @param monitor a progress monitor
*/
@SuppressWarnings("unused")
public void unconfigure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException {
}
/**
* Updates project configuration according project changes.
* <p>
* <i>Can be overwritten by subclass</i>
*
* @param event a project change event
* @param monitor a progress monitor
*/
@SuppressWarnings("unused")
public void mavenProjectChanged(MavenProjectChangedEvent event, IProgressMonitor monitor) throws CoreException {
}
// IMavenProjectChangedListener
public final void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) {
for(int i = 0; i < events.length; i++ ) {
try {
mavenProjectChanged(events[i], monitor);
} catch(CoreException ex) {
log.error(ex.getMessage(), ex);
}
}
}
public String getId() {
if(id == null) {
id = getClass().getName();
}
return id;
}
public String getName() {
return name;
}
// IExecutableExtension
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) {
this.id = config.getAttribute(ATTR_ID);
this.name = config.getAttribute(ATTR_NAME);
}
// TODO move to a helper
public static void addNature(IProject project, String natureId, IProgressMonitor monitor) throws CoreException {
addNature(project, natureId, IResource.KEEP_HISTORY, monitor);
}
/**
* @since 1.3
*/
// TODO move to a helper
public static void addNature(IProject project, String natureId, int updateFlags, IProgressMonitor monitor)
throws CoreException {
if(!project.hasNature(natureId)) {
IProjectDescription description = project.getDescription();
String[] prevNatures = description.getNatureIds();
String[] newNatures = new String[prevNatures.length + 1];
System.arraycopy(prevNatures, 0, newNatures, 1, prevNatures.length);
newNatures[0] = natureId;
description.setNatureIds(newNatures);
project.setDescription(description, updateFlags, monitor);
}
}
/**
* @deprecated this method does not properly join {@link IMavenExecutionContext}, use
* {@link #getMojoParameterValue(String, Class, Plugin, ConfigurationContainer, String)} instead.
*/
@SuppressWarnings("deprecation")
protected <T> T getParameterValue(String parameter, Class<T> asType, MavenSession session, MojoExecution mojoExecution)
throws CoreException {
PluginExecution execution = new PluginExecution();
execution.setConfiguration(mojoExecution.getConfiguration());
return maven.getMojoParameterValue(parameter, asType, session, mojoExecution.getPlugin(), execution,
mojoExecution.getGoal());
}
/**
* @since 1.4
*/
protected <T> T getParameterValue(MavenProject project, String parameter, Class<T> asType,
MojoExecution mojoExecution, IProgressMonitor monitor) throws CoreException {
PluginExecution execution = new PluginExecution();
execution.setConfiguration(mojoExecution.getConfiguration());
return maven.getMojoParameterValue(project, parameter, asType, mojoExecution.getPlugin(), execution,
mojoExecution.getGoal(), monitor);
}
protected void assertHasNature(IProject project, String natureId) throws CoreException {
if(project.getNature(natureId) == null) {
throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1,
Messages.AbstractProjectConfigurator_error_missing_nature + natureId, null));
}
}
@Override
public String toString() {
return getId() + ":" + name; //$NON-NLS-1$
}
public AbstractBuildParticipant getBuildParticipant(IMavenProjectFacade projectFacade, MojoExecution execution,
IPluginExecutionMetadata executionMetadata) {
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(getClass() != obj.getClass()) {
return false;
}
AbstractProjectConfigurator other = (AbstractProjectConfigurator) obj;
if(getId() == null) {
if(other.getId() != null) {
return false;
}
} else if(!getId().equals(other.getId())) {
return false;
}
return true;
}
/**
* Returns list of MojoExecutions this configurator is enabled for.
*/
protected List<MojoExecution> getMojoExecutions(ProjectConfigurationRequest request, IProgressMonitor monitor)
throws CoreException {
IMavenProjectFacade projectFacade = request.getMavenProjectFacade();
Map<String, Set<MojoExecutionKey>> configuratorExecutions = getConfiguratorExecutions(projectFacade);
ArrayList<MojoExecution> executions = new ArrayList<MojoExecution>();
Set<MojoExecutionKey> executionKeys = configuratorExecutions.get(id);
if(executionKeys != null) {
for(MojoExecutionKey key : executionKeys) {
executions.add(projectFacade.getMojoExecution(key, monitor));
}
}
return executions;
}
/**
* @noreference this method is not expected to be used by client directly
*/
public static Map<String, Set<MojoExecutionKey>> getConfiguratorExecutions(IMavenProjectFacade projectFacade) {
Map<String, Set<MojoExecutionKey>> configuratorExecutions = new HashMap<String, Set<MojoExecutionKey>>();
Map<MojoExecutionKey, List<IPluginExecutionMetadata>> executionMapping = projectFacade.getMojoExecutionMapping();
for(Map.Entry<MojoExecutionKey, List<IPluginExecutionMetadata>> entry : executionMapping.entrySet()) {
List<IPluginExecutionMetadata> metadatas = entry.getValue();
if(metadatas != null) {
for(IPluginExecutionMetadata metadata : metadatas) {
if(metadata.getAction() == PluginExecutionAction.configurator) {
String configuratorId = LifecycleMappingFactory.getProjectConfiguratorId(metadata);
if(configuratorId != null) {
Set<MojoExecutionKey> executions = configuratorExecutions.get(configuratorId);
if(executions == null) {
executions = new LinkedHashSet<MojoExecutionKey>();
configuratorExecutions.put(configuratorId, executions);
}
executions.add(entry.getKey());
}
}
}
}
}
return configuratorExecutions;
}
/**
* Returns true if project configuration has changed and running
* {@link #configure(ProjectConfigurationRequest, IProgressMonitor)} is required. Default implementation uses
* {@link Xpp3Dom#equals(Object)} to compare before/after mojo configuration.
*/
public boolean hasConfigurationChanged(IMavenProjectFacade newFacade,
ILifecycleMappingConfiguration oldProjectConfiguration, MojoExecutionKey key, IProgressMonitor monitor) {
try {
Xpp3Dom oldConfiguration = oldProjectConfiguration.getMojoExecutionConfiguration(key);
MojoExecution mojoExecution = newFacade.getMojoExecution(key, monitor);
Xpp3Dom configuration = mojoExecution != null ? mojoExecution.getConfiguration() : null;
return configuration != null ? !configuration.equals(oldConfiguration) : oldConfiguration != null;
} catch(CoreException ex) {
return true; // assume configuration update is required
}
}
}