edu.washington.cs.cupid.scripting.java.CupidScriptingPlugin.java Source code

Java tutorial

Introduction

Here is the source code for edu.washington.cs.cupid.scripting.java.CupidScriptingPlugin.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Todd Schiller.
 * 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:
 *     Todd Schiller - initial API, implementation, and documentation
 ******************************************************************************/
package edu.washington.cs.cupid.scripting.java;

import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

import com.google.common.collect.Lists;

import edu.washington.cs.cupid.CupidPlatform;
import edu.washington.cs.cupid.capability.ChangeNotifier;
import edu.washington.cs.cupid.capability.ICapability;
import edu.washington.cs.cupid.capability.ICapabilityChangeListener;
import edu.washington.cs.cupid.capability.ICapabilityPublisher;
import edu.washington.cs.cupid.scripting.internal.CompilationUnitLocator;
import edu.washington.cs.cupid.scripting.internal.CupidCapabilityLoader;
import edu.washington.cs.cupid.scripting.java.internal.UpdateClasspathJob;

/**
 * Activator for the Cupid Java scripting plug-in.
 * @author Todd Schiller
 */
public final class CupidScriptingPlugin extends AbstractUIPlugin implements ICapabilityPublisher {

    /**
     *  The plug-in ID for the Cupid Java scripting plug-in.
     */
    public static final String PLUGIN_ID = "edu.washington.cs.cupid.scripting.java"; //$NON-NLS-1$

    private static CupidScriptingPlugin plugin;

    private IProject cupidProject = null;

    private static List<ICapability> dynamic = Lists.newArrayList();
    private static ChangeNotifier notifier;

    private JavaProjectManager projectManager = new JavaProjectManager();

    /**
     * The name of the Cupid script project.
     */
    public static final String CUPID_PROJECT = "Cupid";

    private ILog pluginLog;

    /**
     *  Construct the Cupid Java scripting plug-in.
     */
    public CupidScriptingPlugin() {
    }

    @Override
    public void start(final BundleContext context) throws Exception {
        super.start(context);
        plugin = this;
        pluginLog = getLog();
        notifier = new ChangeNotifier();

        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        cupidProject = root.getProject(CUPID_PROJECT);

        try {
            // force cupid to load first?
            CupidPlatform.class.toString();
        } catch (Exception ex) {
            logError("Error loading CupidPlatform", ex);
        }

        if (cupidProject.exists()) {
            new OpenCupidProjectJob().schedule();
        } else {
            new CreateCupidProjectJob().schedule();
        }
    }

    @Override
    public void stop(final BundleContext context) throws Exception {
        plugin = null;
        super.stop(context);

        ResourcesPlugin.getWorkspace().removeResourceChangeListener(projectManager);
    }

    /**
     * Returns the Cupid script project.
     * @return the Cupid script project
     */
    public IProject getCupidProject() {
        return cupidProject;
    }

    /**
     * Returns the Cupid script project.
     * @return the Cupid script project
     */
    public IJavaProject getCupidJavaProject() {
        return JavaCore.create(cupidProject);
    }

    /**
     * @return the shared instance
     */
    public static CupidScriptingPlugin getDefault() {
        return plugin;
    }

    private class CreateCupidProjectJob extends Job {
        public CreateCupidProjectJob() {
            super("Create Cupid Project");
            //super.setRule(cupidProject);
        }

        @Override
        protected IStatus run(final IProgressMonitor monitor) {
            try {
                final int totalWork = 3;
                monitor.beginTask("Create Cupid Project", totalWork);

                cupidProject.create(new SubProgressMonitor(monitor, 1));
                cupidProject.open(new SubProgressMonitor(monitor, 1));
                JavaProjectManager.populateCupidProject(cupidProject, new SubProgressMonitor(monitor, 1));

                ResourcesPlugin.getWorkspace().addResourceChangeListener(projectManager,
                        IResourceChangeEvent.POST_BUILD);
                return Status.OK_STATUS;
            } catch (Exception ex) {
                logError("Unable to create Cupid project in workspace", ex);
                return new Status(Status.ERROR, CupidScriptingPlugin.PLUGIN_ID, "Error creating Cupid project", ex);

            } finally {
                monitor.done();
            }
        }
    }

    private class OpenCupidProjectJob extends Job {
        public OpenCupidProjectJob() {
            super("Open Cupid Project");
            super.setRule(cupidProject);
        }

        @Override
        protected IStatus run(final IProgressMonitor monitor) {
            try {
                monitor.beginTask("Open Cupid Project", 100);

                cupidProject.open(new SubProgressMonitor(monitor, 40));
                ResourcesPlugin.getWorkspace().addResourceChangeListener(projectManager,
                        IResourceChangeEvent.POST_BUILD);
                new UpdateClasspathJob().schedule();
                new LoadCapabilitiesJob().schedule();
                return Status.OK_STATUS;

            } catch (CoreException ex) {
                logError("Unable to open Cupid project in workspace", ex);
                return new Status(Status.ERROR, CupidScriptingPlugin.PLUGIN_ID, "Error opening Cupid project", ex);

            } finally {
                monitor.done();
            }
        }

    }

    private class LoadCapabilitiesJob extends WorkspaceJob {
        public LoadCapabilitiesJob() {
            super("Load Dynamic Capabilities");
        }

        @Override
        public IStatus runInWorkspace(final IProgressMonitor monitor) {
            try {
                SubMonitor progress = SubMonitor.convert(monitor, 100);

                dynamic.clear();

                CompilationUnitLocator finder = new CompilationUnitLocator();
                try {
                    cupidProject.accept(finder);
                } catch (CoreException e) {
                    logError("Error finding changed dynamic class files", e);
                    return new Status(IStatus.ERROR, CupidScriptingPlugin.PLUGIN_ID,
                            "Error finding changed dynamic class files", e);
                }

                progress.setWorkRemaining(70);

                Set<ICompilationUnit> classes = finder.getCapabilityClasses();

                SubMonitor loopProgress = progress.newChild(70).setWorkRemaining(classes.size());

                for (ICompilationUnit clazz : classes) {
                    try {
                        loadDynamicCapability(clazz, true);
                    } catch (Exception e) {
                        logError("Error loading class from file " + simpleName(clazz), e);
                    } catch (Error e) {
                        // expected when there are compilation errors
                    } finally {
                        loopProgress.worked(1);
                    }
                }

                return Status.OK_STATUS;
            } finally {
                monitor.done();
            }
        }
    }

    private String simpleName(final IJavaElement clazz) {
        return clazz.getElementName().substring(0, clazz.getElementName().lastIndexOf('.'));
    }

    private ICapability find(final String name) {
        for (ICapability capability : dynamic) {
            if (capability.getName().equals(name)) {
                return capability;
            }
        }
        return null;
    }

    /**
     * Remove and unregister the capability with the given name
     * @param name the name of the capability
     */
    private void removeCapability(final String name) {
        ICapability capability = find(name);
        if (capability != null) {
            dynamic.remove(capability);
            notifier.onCapabilityRemoved(capability);
        }
    }

    /**
     * Load a class defined by <code>element</code>. If <code>notify</code> and the element is a capability, alert
     * listeners that the capability has been loaded.
     * @param element the element to load
     * @param notify <code>true</code> iff listeners should be alerted
     * @throws Exception if loading the capability fails
     * @throws Error if there are compilation or type resolution errors
     */
    public void loadDynamicCapability(final IJavaElement element, final boolean notify) throws Exception, Error {

        CupidCapabilityLoader loader = new CupidCapabilityLoader(CupidScriptingPlugin.class.getClassLoader());

        if (!element.getResource().exists())
            return;

        Class<?> definition = loader.loadClass(simpleName(element));

        if (!ICapability.class.isAssignableFrom(definition))
            return;

        logInformation("Loaded dynamic capability " + definition.getSimpleName() + " from " + simpleName(element));

        ICapability capability = (ICapability) definition.newInstance();

        removeCapability(capability.getName());

        dynamic.add(capability);

        if (notify) {
            notifier.onCapabilityAdded(capability);
        }
    }

    /**
     * Log an error in the plugin's log.
     * @param msg localized error message
     * @param e the exception
     */
    public void logError(final String msg, final Throwable e) {
        pluginLog.log(new Status(Status.ERROR, CupidScriptingPlugin.PLUGIN_ID, Status.ERROR, msg, e));
    }

    /**
     * Log information in the plugin's log.
     * @param msg localized information message
     */
    public void logInformation(final String msg) {
        pluginLog.log(new Status(Status.INFO, CupidScriptingPlugin.PLUGIN_ID, Status.INFO, msg, null));
    }

    /**
     * Log information in the plugin's log.
     * @param msg localized information message
     */
    public void logWarning(final String msg) {
        pluginLog.log(new Status(Status.WARNING, CupidScriptingPlugin.PLUGIN_ID, Status.WARNING, msg, null));
    }

    @Override
    public synchronized ICapability[] publish() {
        return dynamic.toArray(new ICapability[] {});
    }

    @Override
    public synchronized void addChangeListener(final ICapabilityChangeListener listener) {
        notifier.addChangeListener(listener);
    }

    @Override
    public synchronized void removeChangeListener(final ICapabilityChangeListener listener) {
        notifier.removeChangeListener(listener);
    }

}