Package org.eclim.plugin.jdt.project

Source Code of org.eclim.plugin.jdt.project.JavaProjectManager

/**
* Copyright (C) 2005 - 2014  Eric Van Dewoestine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.eclim.plugin.jdt.project;

import java.io.File;
import java.io.FileInputStream;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import org.eclim.Services;

import org.eclim.command.CommandLine;
import org.eclim.command.Error;
import org.eclim.command.Options;

import org.eclim.plugin.core.project.ProjectManager;

import org.eclim.plugin.core.util.ProjectUtils;
import org.eclim.plugin.core.util.XmlUtils;

import org.eclim.plugin.jdt.PluginResources;

import org.eclim.plugin.jdt.project.classpath.Dependency;
import org.eclim.plugin.jdt.project.classpath.IvyParser;
import org.eclim.plugin.jdt.project.classpath.MvnParser;
import org.eclim.plugin.jdt.project.classpath.Parser;

import org.eclim.plugin.jdt.util.JavaUtils;

import org.eclim.util.IOUtils;

import org.eclim.util.file.FileOffsets;
import org.eclim.util.file.FileUtils;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;

import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModelStatus;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;

import org.eclipse.jdt.internal.core.JavaProject;

import org.eclipse.jdt.internal.ui.wizards.ClassPathDetector;

import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathsBlock;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.CPListElement;

import org.eclipse.jdt.ui.PreferenceConstants;

import org.eclipse.jface.preference.IPreferenceStore;

/**
* Implementation of {@link ProjectManager} for java projects.
*
* @author Eric Van Dewoestine
*/
public class JavaProjectManager
  implements ProjectManager
{
  private static final String PRESERVE = "eclim.preserve";

  private static final String CLASSPATH = ".classpath";
  private static final String CLASSPATH_XSD =
    "/resources/schema/eclipse/classpath.xsd";

  private static final HashMap<String, Parser> PARSERS =
    new HashMap<String, Parser>();
  static{
    PARSERS.put("ivy.xml", new IvyParser());
    PARSERS.put("pom.xml", new MvnParser());
  }

  @Override
  public void create(IProject project, CommandLine commandLine)
    throws Exception
  {
    String depends = commandLine.getValue(Options.DEPENDS_OPTION);
    create(project, depends);
  }

  @Override
  public List<Error> update(IProject project, CommandLine commandLine)
    throws Exception
  {
    String buildfile = commandLine.getValue(Options.BUILD_FILE_OPTION);

    IJavaProject javaProject = JavaUtils.getJavaProject(project);
    javaProject.getResource().refreshLocal(IResource.DEPTH_INFINITE, null);

    // validate that .classpath xml is well formed and valid.
    PluginResources resources = (PluginResources)
      Services.getPluginResources(PluginResources.NAME);
    List<Error> errors = XmlUtils.validateXml(
        javaProject.getProject().getName(),
        CLASSPATH,
        resources.getResource(CLASSPATH_XSD).toString());
    if(errors.size() > 0){
      return errors;
    }

    String dotclasspath = javaProject.getProject().getFile(CLASSPATH)
      .getRawLocation().toOSString();

    // ivy.xml, pom.xml, etc updated.
    if(buildfile != null){
      try{
        IClasspathEntry[] entries = mergeWithBuildfile(javaProject, buildfile);
        errors = setClasspath(javaProject, entries, dotclasspath);
      }catch(IllegalStateException ise){
        errors.add(new Error(ise.getMessage(), buildfile, 1, 1, false));
      }

    // .classpath updated.
    }else{
      // if an exception occurs reading the classpath then eclipse will return a
      // default classpath which we would otherwise then write back into the
      // .classpath file. This hack prevents that and will return a relevent
      // error message as a validation error.
      try{
        ((JavaProject)javaProject).readFileEntriesWithException(null);
      } catch(Exception e) {
        errors.add(new Error(e.getMessage(), dotclasspath, 1, 1, false));
        return errors;
      }

      IClasspathEntry[] entries = javaProject.readRawClasspath();
      errors = setClasspath(javaProject, entries, dotclasspath);
    }

    if(errors.size() > 0){
      return errors;
    }
    return null;
  }

  @Override
  public void refresh(IProject project, CommandLine commandLine)
    throws Exception
  {
  }

  @Override
  public void refresh(IProject project, IFile file)
    throws Exception
  {
  }

  @Override
  public void delete(IProject project, CommandLine commandLine)
    throws Exception
  {
  }

// Project creation methods

  /**
   * Creates a new project.
   *
   * @param project The project.
   * @param depends Comma separated project names this project depends on.
   */
  protected void create(IProject project, String depends)
    throws Exception
  {
    // with scala-ide installed, apparently this needs to be explicitly done
    IProjectDescription desc = project.getDescription();
    if(!desc.hasNature(PluginResources.NATURE)){
      String[] natures = desc.getNatureIds();
      String[] newNatures = new String[natures.length + 1];
      System.arraycopy(natures, 0, newNatures, 0, natures.length);
      newNatures[natures.length] = PluginResources.NATURE;
      desc.setNatureIds(newNatures);
      project.setDescription(desc, new NullProgressMonitor());
    }

    IJavaProject javaProject = JavaCore.create(project);
    ((JavaProject)javaProject).configure();

    if (!project.getFile(CLASSPATH).exists()) {
      ArrayList<IClasspathEntry> classpath = new ArrayList<IClasspathEntry>();
      boolean source = false;
      boolean container = false;

      ClassPathDetector detector = new ClassPathDetector(project, null);
      for (IClasspathEntry entry : detector.getClasspath()){
        if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE){
          source = true;
        } else if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER){
          container = true;
        }
        classpath.add(entry);
      }

      // default source folder
      if (!source){
        IResource src;
        IPreferenceStore store = PreferenceConstants.getPreferenceStore();
        String name = store.getString(PreferenceConstants.SRCBIN_SRCNAME);
        boolean srcBinFolders = store.getBoolean(
            PreferenceConstants.SRCBIN_FOLDERS_IN_NEWPROJ);
        if (srcBinFolders && name.length() > 0) {
          src = javaProject.getProject().getFolder(name);
        } else {
          src = javaProject.getProject();
        }

        classpath.add(
            new CPListElement(
              javaProject, IClasspathEntry.CPE_SOURCE, src.getFullPath(), src)
            .getClasspathEntry());

        File srcPath = new File(
            ProjectUtils.getFilePath(project, src.getFullPath().toString()));
        if (!srcPath.exists()){
          srcPath.mkdirs();
        }
      }

      // default containers
      if (!container){
        for (IClasspathEntry entry : PreferenceConstants.getDefaultJRELibrary()){
          classpath.add(entry);
        }
      }

      // dependencies on other projects
      for (IClasspathEntry entry : createOrUpdateDependencies(javaProject, depends)){
        classpath.add(entry);
      }

      javaProject.setRawClasspath(
          classpath.toArray(new IClasspathEntry[classpath.size()]), null);

      // output location
      IPath output = detector.getOutputLocation();
      if (output == null){
        output = BuildPathsBlock.getDefaultOutputLocation(javaProject);
      }
      javaProject.setOutputLocation(output, null);
    }
    javaProject.makeConsistent(null);
    javaProject.save(null, false);
  }

  /**
   * Creates or updates the projects dependencies on other projects.
   *
   * @param project The project.
   * @param depends The comma seperated list of project names.
   */
  protected IClasspathEntry[] createOrUpdateDependencies(
      IJavaProject project, String depends)
    throws Exception
  {
    if(depends != null){
      String[] dependPaths = StringUtils.split(depends, ',');
      IClasspathEntry[] entries = new IClasspathEntry[dependPaths.length];
      for(int ii = 0; ii < dependPaths.length; ii++){
        IProject theProject = ProjectUtils.getProject(dependPaths[ii]);
        if(!theProject.exists()){
          throw new IllegalArgumentException(Services.getMessage(
              "project.depends.not.found", dependPaths[ii]));
        }
        IJavaProject otherProject = JavaCore.create(theProject);
        entries[ii] = JavaCore.newProjectEntry(otherProject.getPath(), true);
      }
      return entries;
    }
    return new IClasspathEntry[0];
  }

  /**
   * Merges the supplied classpath entries into one.
   *
   * @param entries The array of classpath entry arrays to merge.
   *
   * @return The union of all entry arrays.
   */
  protected IClasspathEntry[] merge(IClasspathEntry[][] entries)
  {
    ArrayList<IClasspathEntry> union = new ArrayList<IClasspathEntry>();
    if(entries != null){
      for(IClasspathEntry[] values : entries){
        if(values != null){
          for(IClasspathEntry entry : values){
            if(!union.contains(entry)){
             union.add(entry);
            }
          }
        }
      }
    }
    return (IClasspathEntry[])union.toArray(new IClasspathEntry[union.size()]);
  }

// Project update methods

  /**
   * Sets the classpath for the supplied project.
   *
   * @param javaProject The project.
   * @param entries The classpath entries.
   * @param classpath The file path of the .classpath file.
   * @return Array of Error or null if no errors reported.
   */
  protected List<Error> setClasspath(
      IJavaProject javaProject, IClasspathEntry[] entries, String classpath)
    throws Exception
  {
    FileOffsets offsets = FileOffsets.compile(classpath);
    String classpathValue = IOUtils.toString(new FileInputStream(classpath));
    ArrayList<Error> errors = new ArrayList<Error>();
    for(IClasspathEntry entry : entries){
      IJavaModelStatus status = JavaConventions
        .validateClasspathEntry(javaProject, entry, true);
      if(!status.isOK()){
        errors.add(createErrorForEntry(
              javaProject, entry, status, offsets, classpath, classpathValue));
      }
    }

    IJavaModelStatus status = JavaConventions.validateClasspath(
        javaProject, entries, javaProject.getOutputLocation());

    // always set the classpathValue anyways, so that the user can correct the
    // file.
    //if(status.isOK() && errors.isEmpty()){
      javaProject.setRawClasspath(entries, null);
      javaProject.makeConsistent(null);
    //}

    if(!status.isOK()){
      errors.add(new Error(status.getMessage(), classpath, 1, 1, false));
    }
    return errors;
  }

  /**
   * Creates an Error from the supplied IJavaModelStatus.
   *
   * @param project The java project.
   * @param entry The classpath entry.
   * @param status The IJavaModelStatus.
   * @param offsets File offsets for the classpath file.
   * @param filename The filename of the error.
   * @param contents The contents of the file as a String.
   * @return The Error.
   */
  protected Error createErrorForEntry(
      IJavaProject project,
      IClasspathEntry entry,
      IJavaModelStatus status,
      FileOffsets offsets,
      String filename,
      String contents)
    throws Exception
  {
    int line = 0;
    int col = 0;

    String path = entry.getPath().toOSString();
    path = path.replaceFirst("^/" + project.getProject().getName() + "/", "");
    Matcher matcher =
      Pattern.compile("path\\s*=(['\"])\\s*\\Q" + path + "\\E\\s*\\1")
        .matcher(contents);
    if(matcher.find()){
      int[] position = offsets.offsetToLineColumn(matcher.start());
      line = position[0];
      col = position[1];
    }

    return new Error(status.getMessage(), filename, line, col, false);
  }

  /**
   * Merge the supplied project's classpath with entries found in the specified
   * build file.
   *
   * @param project The project.
   * @param buildfile The path to the build file (pom.xml, ivy.xml, etc)
   * @return The classpath entries.
   */
  protected IClasspathEntry[] mergeWithBuildfile(
      IJavaProject project, String buildfile)
    throws Exception
  {
    String filename = FileUtils.getBaseName(buildfile);
    Parser parser = PARSERS.get(filename);
    String var = parser.getClasspathVar();
    Dependency[] dependencies = parser.parse(buildfile);

    IWorkspaceRoot root = project.getProject().getWorkspace().getRoot();
    ArrayList<IClasspathEntry> results = new ArrayList<IClasspathEntry>();

    // load the results with all the non library entries.
    IClasspathEntry[] entries = project.getRawClasspath();
    for(IClasspathEntry entry : entries){
      if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY &&
          entry.getEntryKind() != IClasspathEntry.CPE_VARIABLE)
      {
        results.add(entry);
      }else{
        IPath path = entry.getPath();
        String prefix = path != null ? path.segment(0) : null;
        if ((!var.equals(prefix)) || preserve(entry)){
          results.add(entry);
        }
      }
    }

    // merge the dependencies with the classpath entires.
    for(int ii = 0; ii < dependencies.length; ii++){
      IClasspathEntry match = null;
      for(int jj = 0; jj < entries.length; jj++){
        if (entries[jj].getEntryKind() == IClasspathEntry.CPE_LIBRARY ||
            entries[jj].getEntryKind() == IClasspathEntry.CPE_VARIABLE)
        {
          String path = entries[jj].getPath().toOSString();
          String pattern = dependencies[ii].getName() +
            Dependency.VERSION_SEPARATOR;

          // exact match
          if(path.endsWith(dependencies[ii].toString())){
            match = entries[jj];
            results.add(entries[jj]);
            break;

          // different version match
          }else if(path.indexOf(pattern) != -1){
            break;
          }
        }else if(entries[jj].getEntryKind() == IClasspathEntry.CPE_PROJECT){
          String path = entries[jj].getPath().toOSString();
          if(path.endsWith(dependencies[ii].getName())){
            match = entries[jj];
            break;
          }
        }
      }

      if(match == null){
        IClasspathEntry entry = createEntry(root, project, dependencies[ii]);
        results.add(entry);
      }else{
        match = null;
      }
    }

    return (IClasspathEntry[])
      results.toArray(new IClasspathEntry[results.size()]);
  }

  /**
   * Determines if the supplied entry contains attribute indicating that it
   * should not be removed.
   *
   * @param entry The IClasspathEntry
   * @return true to preserve the entry, false otherwise.
   */
  protected boolean preserve(IClasspathEntry entry)
  {
    IClasspathAttribute[] attributes = entry.getExtraAttributes();
    for(int ii = 0; ii < attributes.length; ii++){
      String name = attributes[ii].getName();
      if(PRESERVE.equals(name)){
        return Boolean.parseBoolean(attributes[ii].getValue());
      }
    }
    return false;
  }

  /**
   * Creates the classpath entry.
   *
   * @param root The workspace root.
   * @param project The project to create the dependency in.
   * @param dependency The dependency to create the entry for.
   * @return The classpath entry.
   */
  protected IClasspathEntry createEntry(
      IWorkspaceRoot root, IJavaProject project, Dependency dependency)
    throws Exception
  {
    if(dependency.isVariable()){
      return JavaCore.newVariableEntry(dependency.getPath(), null, null, true);
    }

    return JavaCore.newLibraryEntry(dependency.getPath(), null, null, true);
  }

  /**
   * Determines if the supplied path starts with a variable name.
   *
   * @param path The path to test.
   * @return True if the path starts with a variable name, false otherwise.
   */
  protected boolean startsWithVariable(String path)
  {
    String[] variables = JavaCore.getClasspathVariableNames();
    for(int ii = 0; ii < variables.length; ii++){
      if(path.startsWith(variables[ii])){
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of org.eclim.plugin.jdt.project.JavaProjectManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.