org.maven.ide.eclipse.scala.ScalaProjectConfigurator.java Source code

Java tutorial

Introduction

Here is the source code for org.maven.ide.eclipse.scala.ScalaProjectConfigurator.java

Source

/*******************************************************************************
 * Copyright (c) 2008 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
 *******************************************************************************/

package org.maven.ide.eclipse.scala;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoExecution;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.ClasspathAttribute;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest;
import org.eclipse.m2e.jdt.AbstractJavaProjectConfigurator;
import org.eclipse.m2e.jdt.IClasspathDescriptor;
import org.eclipse.m2e.jdt.IClasspathEntryDescriptor;

//TODO check the jre/java version compliance (>= 1.5)
//TODO check JDT Weaving is enabled (if not enabled, icon of scala file is [J] same as java (and property of  the file display "Type :... Java Source File" )
//TODO check that pom.xml and ScalaLib Container declare the same scala version
//TODO keep sync scala-compiler configuration between pom.xml and scala-plugin ? (sync bi-direction) ?
//TODO ask sonatype/mailing-list about how to retreive maven plugin configuration, like additional sourceDirectory
/**
 * @author Sonatype Inc (http://github.com/sonatype/m2eclipse-scala)
 * @author davidB (http://github.com/davidB)
 * @author germanklf  (http://github.com/germanklf)
 * @author Fred Bricon
 */
public class ScalaProjectConfigurator extends AbstractJavaProjectConfigurator {

    private static final String SCALA_CONTAINER_PATH = "org.scala-ide.sdt.launching.SCALA_CONTAINER";

    private static final String MAVEN_CONTAINER_PATH = "org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER";

    private static String DEPLOYABLE_KEY = "org.eclipse.jst.component.dependency";

    private static String NON_DEPLOYABLE_KEY = "org.eclipse.jst.component.nondependency";

    private Map<String, Integer> mapSourceTypeWeight;

    private Map<String, Integer> mapResourceWeight;

    private Comparator<IClasspathEntry> comparator;

    public ScalaProjectConfigurator() {
        super();

        mapSourceTypeWeight = new HashMap<String, Integer>();
        mapSourceTypeWeight.put("src/main/", 9000);
        mapSourceTypeWeight.put("src/test/", 1000);

        mapResourceWeight = new HashMap<String, Integer>();
        mapResourceWeight.put("java", 100);
        mapResourceWeight.put("resources", -10);

        comparator = new Comparator<IClasspathEntry>() {
            public int compare(IClasspathEntry o1, IClasspathEntry o2) {
                Integer w1 = getWeight(o1);
                Integer w2 = getWeight(o2);
                if (w1 > 0 || w2 > 0) {
                    if (w1.equals(w2))
                        return o1.getPath().toString().compareTo(o2.getPath().toString());
                    else
                        return w1.compareTo(w2) * -1;
                }
                return 0;
            }

            private Integer getWeight(IClasspathEntry ce) {
                int value = 0;
                if (ce.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                    for (Entry<String, Integer> e : mapSourceTypeWeight.entrySet()) {
                        if (ce.getPath().toString().contains(e.getKey())) {
                            value += e.getValue();
                        }
                    }
                    for (Entry<String, Integer> e : mapResourceWeight.entrySet()) {
                        if (ce.getPath().toString().endsWith(e.getKey())) {
                            value += e.getValue();
                        }
                    }
                }
                return value;
            }
        };
    }

    private String scalaNatureId() {
        ScalaPluginIds ids = Activator.getInstance().scalaPluginIds();
        return (ids == null) ? null : ids.natureId;
    }

    //  private boolean shouldManageClasspath() {
    //    Version v4 = new Version(4,0,0);
    //    return v4.compareTo(Activator.getInstance().scalaPluginIds().version) > 0;
    //  }

    @Override
    public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException {
        super.configure(request, monitor);
        String scalaNature = scalaNatureId();
        if (scalaNature == null) {
            //TODO show an alert to user that he should to install scala-ide plugin;
            return;
        }
        try {
            if (request != null) {
                IProject project = request.getProject();
                if (!project.hasNature(scalaNature) && isScalaProject(request.getMavenProjectFacade(), monitor)) {
                    if (!project.hasNature("org.eclipse.jdt.core.javanature")) {
                        addNature(project, "org.eclipse.jdt.core.javanature", monitor);
                    }
                    addNature(project, scalaNature, monitor);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * configure Classpath : contain of "Maven Dependencies" Librairies Container.
     */
    @Override
    public void configureClasspath(IMavenProjectFacade facade, IClasspathDescriptor classpath,
            IProgressMonitor monitor) throws CoreException {
        String scalaNature = scalaNatureId();
        if (scalaNature == null) {
            //TODO show an alert to user that he should to install scala-ide plugin;
            return;
        }
        if (!isLaunchConfigurationCtx()) {
            IProject project = facade.getProject();
            if (isScalaProject(project)) {
                //      if(!project.hasNature(ID_NATURE)) {
                //        addNature(project, ID_NATURE, monitor);
                //      }
                removeScalaFromMavenContainer(classpath);
                sortContainerScalaJre(project, monitor); //
                makeScalaLibDeployable(facade, classpath, monitor);
            }
        }
    }

    // workaround to avoid NPE
    @Override
    protected IPath getFullPath(IMavenProjectFacade facade, File file) {
        if (file == null)
            return null;
        return super.getFullPath(facade, file);
    }

    private void removeScalaFromMavenContainer(final IClasspathDescriptor classpath) {
        classpath.removeEntry(new IClasspathDescriptor.EntryFilter() {
            public boolean accept(IClasspathEntryDescriptor descriptor) {
                boolean back = "org.scala-lang".equals(descriptor.getGroupId());
                //TODO, use content of Scala Library Container instead of hardcoded value
                back = back && ("scala-library".equals(descriptor.getArtifactId())
                        //|| "scala-compiler".equals(descriptor.getArtifactId())
                        || "scala-dbc".equals(descriptor.getArtifactId())
                        || "scala-swing".equals(descriptor.getArtifactId()));
                return back;
            }
        });
    }

    /**
     * Check and reorder Containers : "Scala Lib" should be before "JRE Sys",
     * else 'Run Scala Application' set Boot Entries JRE before Scala and failed with scala.* NotFound Exception.
     * Should already be done when adding nature
     * @see scala.tools.eclipse.Nature#configure()
     */
    private void sortContainerScalaJre(final IProject project, IProgressMonitor monitor) throws CoreException {
        IJavaProject javaProject = JavaCore.create(project);
        IClasspathEntry[] classpathEntries = javaProject.getRawClasspath();

        Boolean sorted = true;
        for (int i = 0; i < classpathEntries.length - 1; i++) {
            if (comparator.compare(classpathEntries[i], classpathEntries[i + 1]) > 0) {
                sorted = false;
                break;
            }
        }

        if (!sorted) {
            List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(classpathEntries.length);
            Collections.addAll(entries, classpathEntries);

            Collections.sort(entries, comparator);
            javaProject.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), monitor);
        }
    }

    private boolean isScalaProject(IProject project) {
        try {
            return project != null && project.isAccessible() && project.hasNature(scalaNatureId());
        } catch (CoreException e) {
            return false;
        }
    }

    private boolean isScalaProject(IMavenProjectFacade facade, IProgressMonitor monitor) throws CoreException {
        List<Plugin> plugins = facade.getMavenProject(monitor).getBuildPlugins();
        if (plugins != null) {
            for (Plugin plugin : plugins) {
                if (isMavenBundlePluginMojo(plugin) && !plugin.getExecutions().isEmpty()) {
                    return true;
                }
            }
        }
        return false;
    }

    protected boolean isLaunchConfigurationCtx() {
        for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
            if ("launch".equals(e.getMethodName())) {
                return true;
            }
        }
        return false;
    }

    protected boolean isMavenBundlePluginMojo(MojoExecution execution) {
        return isMavenBundlePluginMojo(execution.getGroupId(), execution.getArtifactId());
    }

    private boolean isMavenBundlePluginMojo(Plugin plugin) {
        return isMavenBundlePluginMojo(plugin.getGroupId(), plugin.getArtifactId());
    }

    private boolean isMavenBundlePluginMojo(String groupId, String artifactId) {
        return ("org.scala-tools".equals(groupId) && "maven-scala-plugin".equals(artifactId))
                || ("net.alchim31.maven".equals(groupId) && "scala-maven-plugin".equals(artifactId));
    }

    @SuppressWarnings("restriction")
    private void makeScalaLibDeployable(IMavenProjectFacade facade, IClasspathDescriptor classpath,
            IProgressMonitor monitor) throws CoreException {

        IJavaProject javaProject = JavaCore.create(facade.getProject());
        if (javaProject == null) {
            return;
        }

        IClasspathEntry scalaLibrary = getContainer(javaProject, SCALA_CONTAINER_PATH);

        if (scalaLibrary == null) {
            //Really nothing to do here
            return;
        }

        IClasspathEntry mavenLibrary = getContainer(javaProject, MAVEN_CONTAINER_PATH);

        IClasspathAttribute deployableAttribute = getDeployableAttribute(mavenLibrary);
        //If the Maven Classpath Container is set to be deployed in WTP, then do the same for the Scala one
        if (deployableAttribute != null) {
            //Add the deployable attribute only if it's not set already.
            if (getDeployableAttribute(scalaLibrary) == null) {
                addDeployableAttribute(javaProject, deployableAttribute, monitor);
            }
        }

    }

    private static void addDeployableAttribute(IJavaProject javaProject, IClasspathAttribute deployableAttribute,
            IProgressMonitor monitor) throws JavaModelException, CoreException {
        if (javaProject == null)
            return;
        ClasspathContainerInitializer scalaInitializer = JavaCore
                .getClasspathContainerInitializer(SCALA_CONTAINER_PATH);
        if (scalaInitializer == null)
            return;
        IPath scalaContainerPath = Path.fromPortableString(SCALA_CONTAINER_PATH);
        Boolean updateAble = scalaInitializer.canUpdateClasspathContainer(scalaContainerPath, javaProject);
        final IClasspathContainer scalaLibrary = JavaCore.getClasspathContainer(scalaContainerPath, javaProject);
        final IClasspathEntry[] cpEntries = scalaLibrary.getClasspathEntries();

        for (int i = 0; i < cpEntries.length; i++) {
            IClasspathEntry cpe = cpEntries[i];
            LinkedHashMap<String, IClasspathAttribute> attrs = new LinkedHashMap<String, IClasspathAttribute>();
            for (IClasspathAttribute attr : cpe.getExtraAttributes()) {
                //Keep all existing attributes except the non_deployable key
                if (!attr.getName().equals(NON_DEPLOYABLE_KEY)) {
                    attrs.put(attr.getName(), attr);
                }
            }
            attrs.put(deployableAttribute.getName(), deployableAttribute);
            IClasspathAttribute[] newAttrs = attrs.values().toArray(new IClasspathAttribute[attrs.size()]);
            cpEntries[i] = JavaCore.newLibraryEntry(cpe.getPath(), cpe.getSourceAttachmentPath(),
                    cpe.getSourceAttachmentRootPath(), cpe.getAccessRules(), newAttrs, cpe.isExported());
        }

        IClasspathContainer candidateScalaContainer = new IClasspathContainer() {
            public IPath getPath() {
                return scalaLibrary.getPath();
            }

            public IClasspathEntry[] getClasspathEntries() {
                return cpEntries;
            }

            public String getDescription() {
                return scalaLibrary.getDescription();
            }

            public int getKind() {
                return scalaLibrary.getKind();
            }
        };

        if (updateAble) {
            scalaInitializer.requestClasspathContainerUpdate(scalaContainerPath, javaProject,
                    candidateScalaContainer);
        } else {
            IJavaProject[] jPArray = { javaProject };
            IClasspathContainer[] cpArray = { candidateScalaContainer };
            JavaCore.setClasspathContainer(scalaContainerPath, jPArray, cpArray, null);
        }
    }

    private static IClasspathEntry getContainer(IJavaProject javaProject, String containerPath)
            throws JavaModelException {
        IClasspathEntry[] cp = javaProject.getRawClasspath();
        for (int i = 0; i < cp.length; i++) {
            if (IClasspathEntry.CPE_CONTAINER == cp[i].getEntryKind()
                    && containerPath.equals(cp[i].getPath().lastSegment())) {
                return cp[i];
            }
        }
        return null;
    }

    private static IClasspathAttribute getDeployableAttribute(IClasspathEntry library) {
        if (library == null) {
            return null;
        }

        IClasspathAttribute[] attributes = library.getExtraAttributes();
        if (attributes == null || attributes.length == 0) {
            return null;
        }

        for (IClasspathAttribute attr : attributes) {
            if (DEPLOYABLE_KEY.equals(attr.getName())) {
                return new ClasspathAttribute(attr.getName(), attr.getValue());
            }
        }
        return null;
    }
}