/*******************************************************************************
* Copyright (c) 2012 VMWare, 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:
* VMWare, Inc. - initial API and implementation
*******************************************************************************/
package org.grails.ide.eclipse.maven;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoExecution;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.m2e.core.lifecyclemapping.model.IPluginExecutionMetadata;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.configurator.AbstractBuildParticipant;
import org.eclipse.m2e.core.project.configurator.ILifecycleMappingConfiguration;
import org.eclipse.m2e.core.project.configurator.MojoExecutionBuildParticipant;
import org.eclipse.m2e.core.project.configurator.MojoExecutionKey;
import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest;
import org.eclipse.m2e.jdt.IClasspathDescriptor;
import org.eclipse.m2e.jdt.IClasspathManager;
import org.eclipse.m2e.jdt.IJavaProjectConfigurator;
import org.eclipse.m2e.jdt.internal.AbstractJavaProjectConfigurator;
import org.grails.ide.eclipse.commands.GrailsCommandUtils;
import org.grails.ide.eclipse.core.internal.GrailsResourceUtil;
import org.grails.ide.eclipse.core.internal.classpath.GrailsClasspathContainer;
import org.grails.ide.eclipse.core.internal.classpath.GrailsClasspathUtils;
import org.grails.ide.eclipse.core.internal.classpath.PerProjectDependencyDataCache;
import org.grails.ide.eclipse.core.internal.classpath.SourceFolderJob;
import org.grails.ide.eclipse.core.internal.plugins.GrailsCore;
import org.grails.ide.eclipse.core.internal.plugins.PerProjectPluginCache;
import org.grails.ide.eclipse.core.launch.ClasspathLocalizer;
import org.grails.ide.eclipse.core.launch.EclipsePluginClasspathEntry;
import org.grails.ide.eclipse.core.launch.GrailsLaunchArgumentUtils;
import org.grails.ide.eclipse.core.model.GrailsInstallManager;
import org.grails.ide.eclipse.core.model.GrailsVersion;
import org.grails.ide.eclipse.runtime.shared.SharedLaunchConstants;
import org.springsource.ide.eclipse.commons.frameworks.core.legacyconversion.IConversionConstants;
public class GrailsProjectConfigurator extends AbstractJavaProjectConfigurator implements IJavaProjectConfigurator {
@Override
public void configure(ProjectConfigurationRequest request,
IProgressMonitor monitor) throws CoreException {
IProject project = request.getProject();
addJavaNature(project, monitor);
IJavaProject javaProject = JavaCore.create(project);
boolean noContainer = !GrailsClasspathUtils.hasClasspathContainer(javaProject);
boolean hasOldContainer = GrailsClasspathUtils.hasOldClasspathContainer(javaProject);
IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(Arrays.asList(rawClasspath));
if (hasOldContainer) {
for (Iterator<IClasspathEntry> entryIter = entries.iterator(); entryIter.hasNext();) {
IClasspathEntry entry = entryIter.next();
if (entry.getPath().toPortableString().equals(IConversionConstants.GRAILS_OLD_CONTAINER)) {
entryIter.remove();
break;
}
}
}
if (noContainer) {
entries.add(JavaCore.newContainerEntry(
GrailsClasspathContainer.CLASSPATH_CONTAINER_PATH, null,
null, false));
}
// also remove any grails source folders that have the grails classpath attribute
for (Iterator<IClasspathEntry> entryIter = entries.iterator(); entryIter.hasNext();) {
IClasspathEntry entry = entryIter.next();
if (GrailsResourceUtil.isGrailsClasspathEntry(entry) && !GrailsResourceUtil.hasClasspathAttribute(entry, IClasspathManager.POMDERIVED_ATTRIBUTE)) {
entryIter.remove();
}
}
javaProject.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), monitor);
super.configure(request, monitor);
}
public void configureRawClasspath(ProjectConfigurationRequest request,
IClasspathDescriptor classpath, IProgressMonitor monitor)
throws CoreException {
IProject project = request.getProject();
GrailsCommandUtils.ensureNaturesAndBuilders(project);
IJavaProject javaProject = JavaCore.create(project);
// make sure that we have a dependency file
String descriptorName = GrailsClasspathUtils.getDependencyDescriptorName(project);
File f = new File(descriptorName);
if (!f.exists()) {
throw new CoreException(new Status(IStatus.ERROR, GrailsMavenActivator.PLUGIN_ID, "Could not find the grails dependency file." +
" This probably means that there is a bad dependency in the pom file.", new Exception()));
}
// ensure that the dependency and plugin data is forgotten
GrailsCore.get().connect(project, PerProjectDependencyDataCache.class).refreshData();
GrailsCore.get().connect(project, PerProjectPluginCache.class).refreshDependencyCache();
// We now know that all of the dependency information is up to date
// can't call refresh dependencies directly since that will mess up the source folders
// source folders must be seen as coming from maven, not grails-ide
SourceFolderJob folderJob = new SourceFolderJob(javaProject);
List<IClasspathEntry> sourceEntries = folderJob.findSourceEntries(monitor);
for (IClasspathEntry entry : sourceEntries) {
classpath.addEntry(entry);
}
folderJob.fixCharSets(monitor);
GrailsCommandUtils.deleteOutOfSynchPlugins(project);
// we can ask GrailsClasspathContainer to refresh its dependencies.
GrailsClasspathContainer container = GrailsClasspathUtils.getClasspathContainer(javaProject);
// reparse classpath entries from dependencies file on next request
if (container != null) {
container.invalidate();
container.getClasspathEntries();
}
}
@Override
protected void addJavaProjectOptions(Map<String, String> options,
ProjectConfigurationRequest request, IProgressMonitor monitor)
throws CoreException {
// because of the use of the extension, we cannot get the source and target info from
// super. must calculate it ourselves.
Plugin plugin = request.getMavenProject().getPlugin("org.apache.maven.plugins:maven-compiler-plugin");
String source = null;
String target = null;
if (plugin != null) {
Object configuration = plugin.getConfiguration();
if (configuration instanceof Xpp3Dom) {
Xpp3Dom xml = (Xpp3Dom) configuration;
Xpp3Dom sourceChild = xml.getChild("source");
if (sourceChild != null) {
source = sourceChild.getValue();
if(source.equals("5")) {
source = "1.5";
} else if(source.equals("6")) {
source = "1.6";
} else if(source.equals("7")) {
source = "1.7";
}
}
Xpp3Dom targetChild = xml.getChild("target");
if (targetChild != null) {
target = targetChild.getValue();
if(target.equals("5")) {
target = "1.5";
} else if(target.equals("6")) {
target = "1.6";
} else if(target.equals("7")) {
target = "1.7";
}
}
}
}
if (source == null) {
source = "1.6";
}
if (target == null) {
target = "1.6";
}
options.put(JavaCore.COMPILER_SOURCE, source);
options.put(JavaCore.COMPILER_COMPLIANCE, source);
options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, target);
}
@Override
public boolean hasConfigurationChanged(IMavenProjectFacade newFacade,
ILifecycleMappingConfiguration oldProjectConfiguration,
MojoExecutionKey key, IProgressMonitor monitor) {
IProject project = newFacade.getProject();
try {
Xpp3Dom origOldConfiguration = oldProjectConfiguration == null ? null : oldProjectConfiguration.getMojoExecutionConfiguration(key);
Xpp3Dom oldConfiguration = augmentConfiguration(project, origOldConfiguration);
MojoExecution mojoExecution = newFacade.getMojoExecution(key, monitor);
Xpp3Dom origNewConfigration = mojoExecution == null ? null : new Xpp3Dom(mojoExecution.getConfiguration());
Xpp3Dom newConfigration = augmentConfiguration(project, origNewConfigration);
return newConfigration != null ? !newConfigration.equals(oldConfiguration) : oldConfiguration != null;
} catch(CoreException ex) {
return true; // assume configuration update is required
}
}
@Override
public AbstractBuildParticipant getBuildParticipant(
IMavenProjectFacade projectFacade, MojoExecution execution,
IPluginExecutionMetadata executionMetadata) {
if ("maven-compile".equals(execution.getGoal())) {
IProject project = projectFacade.getProject();
// delete old dependency info will be recreated
String descriptorName = GrailsClasspathUtils.getDependencyDescriptorName(project);
File f = new File(descriptorName);
f.delete();
Xpp3Dom newConfiguration = augmentConfiguration(project, execution.getConfiguration());
execution.setConfiguration(newConfiguration);
}
return new MojoExecutionBuildParticipant(execution, false, true);
}
private Xpp3Dom augmentConfiguration(IProject project, Xpp3Dom orig) {
if (orig == null) {
return null;
}
Xpp3Dom configuration = new Xpp3Dom(orig);
// extra classpath entry
Xpp3Dom node = configuration.getChild("extraClasspathEntries");
if (node == null) {
node = new Xpp3Dom("extraClasspathEntries");
configuration.addChild(node);
}
ClasspathLocalizer localizer = new ClasspathLocalizer();
GrailsVersion version = GrailsVersion.getEclipseGrailsVersion(project);
String pluginId = GrailsLaunchArgumentUtils.getRuntimeBundleFor(version);
List<String> extraCp = localizer.localizeClasspath(
new EclipsePluginClasspathEntry(pluginId, null),
new EclipsePluginClasspathEntry("org.grails.ide.eclipse.runtime.shared", null));
StringBuilder sb = new StringBuilder();
for (String cpEntry : extraCp) {
sb.append(cpEntry.replaceAll(",", "\\,")).append(",");
}
node.setValue(sb.toString());
// build listeners
node = configuration.getChild("grailsBuildListener");
if (node == null) {
node = new Xpp3Dom("grailsBuildListener");
configuration.addChild(node);
}
node.setValue(SharedLaunchConstants.DependencyExtractingBuildListener_CLASS);
// dependency file location
node = configuration.getChild("dependencyFileLocation");
if (node == null) {
node = new Xpp3Dom("dependencyFileLocation");
configuration.addChild(node);
}
node.setValue(GrailsClasspathUtils.getDependencyDescriptorName(project));
node = configuration.getChild("fork");
if (node == null) {
node = new Xpp3Dom("fork");
configuration.addChild(node);
}
// must run in forked mode
node.setValue("true");
return configuration;
}
public void configureClasspath(IMavenProjectFacade facade,
IClasspathDescriptor classpath, IProgressMonitor monitor)
throws CoreException {
// do nuthin
}
}