org.eclim.plugin.dltk.project.DltkProjectManager.java Source code

Java tutorial

Introduction

Here is the source code for org.eclim.plugin.dltk.project.DltkProjectManager.java

Source

/**
 * Copyright (C) 2005 - 2013  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.dltk.project;

import java.io.FileInputStream;

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

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

import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.OptionBuilder;

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.dltk.PluginResources;

import org.eclim.util.IOUtils;
import org.eclim.util.StringUtils;

import org.eclim.util.file.FileOffsets;

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

import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IModelStatus;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.SourceParserUtil;

import org.eclipse.dltk.internal.core.BuildpathEntry;

import org.eclipse.dltk.internal.ui.wizards.BuildpathDetector;

import org.eclipse.dltk.launching.IInterpreterInstall;
import org.eclipse.dltk.launching.IInterpreterInstallType;
import org.eclipse.dltk.launching.ScriptRuntime;

/**
 * Implementation of {@link ProjectManager} for dltk projects.
 *
 * @author Eric Van Dewoestine
 */
public abstract class DltkProjectManager implements ProjectManager {
    private static final String BUILDPATH = ".buildpath";
    private static final String BUILDPATH_XSD = "/resources/schema/eclipse/buildpath.xsd";

    @Override
    @SuppressWarnings("static-access")
    public void create(IProject project, CommandLine commandLine) throws Exception {
        String[] args = commandLine.getValues(Options.ARGS_OPTION);
        GnuParser parser = new GnuParser();
        org.apache.commons.cli.Options options = new org.apache.commons.cli.Options();
        options.addOption(OptionBuilder.hasArg().withLongOpt("interpreter").create());
        org.apache.commons.cli.CommandLine cli = parser.parse(options, args);

        IInterpreterInstall interpreter = null;
        if (cli.hasOption("interpreter")) {
            String interpreterName = cli.getOptionValue("interpreter");
            IInterpreterInstallType[] types = ScriptRuntime.getInterpreterInstallTypes(getNatureId());

            loop: for (IInterpreterInstallType type : types) {
                IInterpreterInstall[] installs = type.getInterpreterInstalls();
                for (IInterpreterInstall install : installs) {
                    if (install.getName().equals(interpreterName)) {
                        interpreter = install;
                        break loop;
                    }
                }
            }

            if (interpreter == null) {
                throw new IllegalArgumentException(
                        Services.getMessage("interpreter.name.not.found", interpreterName));
            }
        }

        String dependsString = commandLine.getValue(Options.DEPENDS_OPTION);

        IScriptProject scriptProject = DLTKCore.create(project);

        if (!project.getFile(BUILDPATH).exists()) {
            IDLTKLanguageToolkit toolkit = getLanguageToolkit(getNatureId());
            BuildpathDetector detector = new BuildpathDetector(project, toolkit);
            detector.detectBuildpath(null);
            IBuildpathEntry[] detected = detector.getBuildpath();

            // remove any entries the detector may have added that are not valid for
            // this project (currently happens on php projects with the
            // org.eclipse.dltk.launching.INTERPRETER_CONTAINER entry).
            ArrayList<IBuildpathEntry> entries = new ArrayList<IBuildpathEntry>();
            for (IBuildpathEntry entry : detected) {
                IModelStatus status = BuildpathEntry.validateBuildpathEntry(scriptProject, entry, true);
                if (status.isOK()) {
                    entries.add(entry);
                }
            }
            detected = entries.toArray(new IBuildpathEntry[entries.size()]);

            IBuildpathEntry[] depends = createOrUpdateDependencies(scriptProject, dependsString);

            IBuildpathEntry[] buildpath = merge(new IBuildpathEntry[][] { detected, depends });
            //scriptProject.readRawClasspath(), detected, depends, container

            scriptProject.setRawBuildpath(buildpath, null);
        }

        if (interpreter != null) {
            IBuildpathEntry[] buildpath = scriptProject.getRawBuildpath();
            int containerIndex = 0;
            for (int i = 0; i < buildpath.length; i++) {
                if (buildpath[i].getEntryKind() == IBuildpathEntry.BPE_CONTAINER) {
                    containerIndex = i;
                    break;
                }
            }

            if (containerIndex == 0) {
                throw new RuntimeException("No container buildpath entry found.");
            }

            IBuildpathEntry container = buildpath[containerIndex];
            buildpath[containerIndex] = DLTKCore.newContainerEntry(
                    ScriptRuntime.newInterpreterContainerPath(interpreter), container.getAccessRules(),
                    container.getExtraAttributes(), container.isExported());
            scriptProject.setRawBuildpath(buildpath, null);
        }

        scriptProject.makeConsistent(null);
        scriptProject.save(null, false);
    }

    @Override
    public List<Error> update(IProject project, CommandLine commandLine) throws Exception {
        IScriptProject scriptProject = DLTKCore.create(project);
        scriptProject.getResource().refreshLocal(IResource.DEPTH_INFINITE, null);

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

        String dotbuildpath = scriptProject.getProject().getFile(BUILDPATH).getRawLocation().toOSString();

        IBuildpathEntry[] entries = scriptProject.readRawBuildpath();
        FileOffsets offsets = FileOffsets.compile(dotbuildpath);
        String buildpath = IOUtils.toString(new FileInputStream(dotbuildpath));
        errors = new ArrayList<Error>();
        for (IBuildpathEntry entry : entries) {
            IModelStatus status = BuildpathEntry.validateBuildpathEntry(scriptProject, entry, true);
            if (!status.isOK()) {
                errors.add(createErrorForEntry(entry, status, offsets, dotbuildpath, buildpath));
            }
        }

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

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

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

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

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

    /**
     * Get the language toolkit to use.
     *
     * @param natureId The nature id to get the toolkit for.
     * @return The IDLTKLanguageToolkit to use.
     */
    public IDLTKLanguageToolkit getLanguageToolkit(String natureId) {
        return DLTKLanguageManager.getLanguageToolkit(natureId);
    }

    /**
     * Abstract method for subclasses to override which provides the appropriate
     * dltk nature id.
     *
     * @return The nature.
     */
    public abstract String getNatureId();

    /**
     * Creates an Error from the supplied IModelStatus.
     *
     * @param entry The build path entry.
     * @param status The IModelStatus.
     * @param offsets File offsets for the buildpath file.
     * @param filename The filename of the error.
     * @param contents The contents of the file as a String.
     * @return The Error.
     */
    protected Error createErrorForEntry(IBuildpathEntry entry, IModelStatus status, FileOffsets offsets,
            String filename, String contents) throws Exception {
        int line = 1;
        int col = 1;

        String path = entry.getPath().toOSString();
        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);
    }

    /**
     * Creates or updates the projects dependencies on other projects.
     *
     * @param project The project.
     * @param depends The comma seperated list of project names.
     */
    protected IBuildpathEntry[] createOrUpdateDependencies(IScriptProject project, String depends)
            throws Exception {
        if (depends != null) {
            String[] dependPaths = StringUtils.split(depends, ',');
            IBuildpathEntry[] entries = new IBuildpathEntry[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]));
                }
                IScriptProject otherProject = DLTKCore.create(theProject);
                entries[ii] = DLTKCore.newProjectEntry(otherProject.getPath(), true);
            }
            return entries;
        }
        return new IBuildpathEntry[0];
    }

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