ccw.CCWPlugin.java Source code

Java tutorial

Introduction

Here is the source code for ccw.CCWPlugin.java

Source

/*******************************************************************************
 * Copyright (c) 2009 Casey Marshall and others.
 * 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: 
 *    Casey Marshall  - initial API and implementation
 *    Thomas Ettinger - paren matching generic code
 *    Stephan Muehlstrasser - preference support for syntax coloring
 *******************************************************************************/
package ccw;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;

import ccw.editors.clojure.IScanContext;
import ccw.launching.LaunchUtils;
import ccw.nature.AutomaticNatureAdder;
import ccw.preferences.PreferenceConstants;
import ccw.preferences.SyntaxColoringHelper;
import ccw.repl.REPLView;
import ccw.repl.SafeConnection;
import ccw.util.BundleUtils;
import ccw.util.DisplayUtil;
import ccw.util.ITracer;
import ccw.util.NullTracer;
import ccw.util.Tracer;
import clojure.lang.Keyword;
import clojure.lang.Var;

/**
 * The activator class controls the plug-in life cycle
 */
public class CCWPlugin extends AbstractUIPlugin {

    /** The plug-in ID */
    public static final String PLUGIN_ID = "ccw.core";

    /** Leiningen project nature */
    public static final String LEININGEN_NATURE_ID = "ccw.leiningen.nature";

    /** 
     * @param swtKey a key from SWT.COLOR_xxx
     * @return a system managed color (callers must not dispose 
     *         the color themselves)
     */
    public static Color getSystemColor(int swtKey) {
        return Display.getDefault().getSystemColor(swtKey);
    }

    //SHOULD LOG INFO / WARN / ERROR WHEN THE APPROPRIATE FLAGS ARE SET SO THAT ONE DOES NOT HAVE
    //TO GO FROM ONE FILE TO ANOTHER
    //ALSO CONSIDER VARIANTS FOR STACKTRACE, 
    //RAW STRING (no format), etc.

    /** The shared instance */
    private static CCWPlugin plugin;

    private ColorRegistry colorCache;

    private FontRegistry fontRegistry;

    private ServerSocket ackREPLServer;

    private AutomaticNatureAdder synchronizedNatureAdapter;

    private ITracer tracer = NullTracer.INSTANCE;

    public static ITracer getTracer() {
        CCWPlugin plugin = getDefault();
        if (plugin != null && plugin.tracer != null)
            return plugin.tracer;
        else
            return NullTracer.INSTANCE;
    }

    public synchronized void startREPLServer() {
        if (ackREPLServer == null) {
            try {
                // TODO use ClojureOSGi.withBundle instead
                Var startServer = BundleUtils.requireAndGetVar(getBundle().getSymbolicName(),
                        "clojure.tools.nrepl.server/start-server");
                Object defaultHandler = BundleUtils.requireAndGetVar(getBundle().getSymbolicName(),
                        "clojure.tools.nrepl.server/default-handler").invoke();
                Object handler = BundleUtils
                        .requireAndGetVar(getBundle().getSymbolicName(), "clojure.tools.nrepl.ack/handle-ack")
                        .invoke(defaultHandler);
                ackREPLServer = (ServerSocket) ((Map) startServer.invoke(Keyword.intern("handler"), handler))
                        .get(Keyword.intern("server-socket"));
                CCWPlugin.log("Started ccw nREPL server: nrepl://127.0.0.1:" + ackREPLServer.getLocalPort());
            } catch (Exception e) {
                CCWPlugin.logError("Could not start plugin-hosted REPL server", e);
                throw new RuntimeException("Could not start plugin-hosted REPL server", e);
            }
        }
    }

    public int getREPLServerPort() {
        if (ackREPLServer == null) {
            startREPLServer();
        }
        return ackREPLServer.getLocalPort();
    }

    public CCWPlugin() {
        System.out.println("CCWPlugin instanciated");
    }

    public void start(BundleContext context) throws Exception {
        super.start(context);
        System.out.println("CCWPlugin.start: ENTER");
        plugin = this;

        context.addBundleListener(new BundleListener() {

            @Override
            public void bundleChanged(BundleEvent evt) {
                if (evt.getBundle() == CCWPlugin.this.getBundle() && evt.getType() == BundleEvent.STARTED) {

                    tracer = new Tracer(evt.getBundle().getBundleContext(), TraceOptions.getTraceOptions());

                    // We immediately give control back to the OSGi framework application
                    // by starting the code in a new thread
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            // Some Eclipse plugins, such as LaunchingResourceManager
                            // call PlatformUI.getWorbench() and checking for null,
                            // even though null is not a valid return value
                            // (instead, an exception is thrown), resulting
                            // in the whole Eclipse to collapse.
                            // Let's protect this code once and for all by ensuring
                            // That the Workbench has been initialized before calling
                            // the initialization code
                            while (!PlatformUI.isWorkbenchRunning()) {
                                try {
                                    if (CCWPlugin.this.getBundle().getState() != Bundle.ACTIVE)
                                        return;
                                    Thread.sleep(200);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }

                            // The Workbench may not be initialized, causing weird issues e.g. in Kepler
                            // WorkbenchThemeManager.getInstance() in Kepler for instance does
                            // not ensure the instance is created in the UI Thread
                            // Once the Workbench is initialized, WorkbenchThemeManager & all
                            // are ensured to be created, so this removes a bunch of plugin startup
                            // race conditions
                            final IWorkbench workbench = PlatformUI.getWorkbench();
                            final AtomicBoolean isWorkbenchInitialized = new AtomicBoolean();
                            while (true) {
                                workbench.getDisplay().syncExec(new Runnable() {
                                    @Override
                                    public void run() {
                                        if (workbench.getActiveWorkbenchWindow() != null) {
                                            // we've got an active window, so workbench is initialized!
                                            isWorkbenchInitialized.set(true);
                                        }
                                    }
                                });
                                if (isWorkbenchInitialized.get()) {
                                    break;
                                } else {
                                    try {
                                        Thread.sleep(200);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }

                            // Here, the workbench is initialized
                            if (System.getProperty("ccw.autostartnrepl") != null) {
                                try {
                                    startREPLServer();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }

                            getNatureAdapter().start();
                        }

                    }).start();
                }
            }
        });

        System.out.println("CCWPlugin.start: EXIT");
    }

    private synchronized AutomaticNatureAdder getNatureAdapter() {
        if (synchronizedNatureAdapter == null) {
            synchronizedNatureAdapter = new AutomaticNatureAdder();
        }
        return synchronizedNatureAdapter;
    }

    private synchronized void createColorCache() {
        if (colorCache == null) {
            colorCache = new ColorRegistry(getWorkbench().getDisplay());
            colorCache.put("ccw.repl.expressionBackground", new RGB(0xf0, 0xf0, 0xf0));
        }
    }

    /**
     * Must be called from the UI thread only
     */
    public ColorRegistry getColorCache() {
        if (colorCache == null) {
            createColorCache();
        }
        return colorCache;
    }

    private synchronized void createFontRegistry() {
        if (fontRegistry == null) {
            DisplayUtil.syncExec(new Runnable() {
                public void run() {
                    fontRegistry = new FontRegistry(getWorkbench().getDisplay());

                    // Forces initializations
                    fontRegistry.getItalic(""); // readOnlyFont
                    fontRegistry.getBold(""); // abstractFont
                }
            });
        }
    }

    private FontRegistry getFontRegistry() {
        if (fontRegistry == null) {
            createFontRegistry();
        }
        return fontRegistry;
    }

    public final Font getJavaSymbolFont() {
        return getFontRegistry().getItalic("");
    }

    private IPreferenceStore prefs;

    /**
     * Create a preference store combined from the Clojure, the EditorsUI and
     * the PlatformUI preference stores to inherit all the default text editor
     * settings from the Eclipse preferences.
     * 
     * <p>Beware, the combined preference store can only be instanciated from the
     *    UI Thread.</p>
     * 
     * @return the combined preference store.
     */
    public IPreferenceStore getCombinedPreferenceStore() {
        if (prefs == null) {
            prefs = new ChainedPreferenceStore(new IPreferenceStore[] { CCWPlugin.getDefault().getPreferenceStore(),
                    EditorsUI.getPreferenceStore(), PlatformUI.getPreferenceStore() });
        }
        return prefs;
    }

    public void stop(BundleContext context) throws Exception {

        // We don't remove colors when deregistered, because, well, we don't have a
        // corresponding method on the ColorRegistry instance!
        // We also don't remove fonts when deregistered
        stopREPLServer();

        this.getNatureAdapter().stop();

        plugin = null;
        super.stop(context);
    }

    private void stopREPLServer() {
        if (ackREPLServer != null) {
            try {
                ackREPLServer.close();
            } catch (IOException e) {
                logError("Error while trying to close ccw internal nrepl server", e);
            }
        }
    }

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

    public static void logError(String msg) {
        getTracer().trace(TraceOptions.LOG_ERROR, "ERROR  - " + msg);
        plugin.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, msg));
    }

    public static void logError(String msg, Throwable e) {
        getTracer().trace(TraceOptions.LOG_ERROR, e, "ERROR  - " + msg);
        plugin.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, msg, e));
    }

    public static void logError(Throwable e) {
        getTracer().trace(TraceOptions.LOG_ERROR, e, "ERROR  - ");
        plugin.getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage(), e));
    }

    public static IStatus createErrorStatus(String message, Throwable e) {
        return new Status(IStatus.ERROR, PLUGIN_ID, message, e);
    }

    public static IStatus createErrorStatus(String message) {
        return new Status(IStatus.ERROR, PLUGIN_ID, message);
    }

    public static void logWarning(String msg) {
        getTracer().trace(TraceOptions.LOG_WARNING, "WARNING - " + msg);
        plugin.getLog().log(new Status(IStatus.WARNING, PLUGIN_ID, msg));
    }

    public static void logWarning(String msg, Throwable e) {
        getTracer().trace(TraceOptions.LOG_WARNING, e, "WARNING - " + msg);
        plugin.getLog().log(new Status(IStatus.WARNING, PLUGIN_ID, msg, e));
    }

    public static void logWarning(Throwable e) {
        getTracer().trace(TraceOptions.LOG_WARNING, e);
        plugin.getLog().log(new Status(IStatus.WARNING, PLUGIN_ID, e.getMessage(), e));
    }

    public static void log(String msg) {
        getTracer().trace(TraceOptions.LOG_INFO, "INFO   - " + msg);
        plugin.getLog().log(new Status(IStatus.INFO, PLUGIN_ID, msg));
    }

    @Override
    protected void initializeImageRegistry(ImageRegistry reg) {
        reg.put(NS, ImageDescriptor.createFromURL(getBundle().getEntry("/icons/jdt/package_obj.gif")));
        reg.put(PUBLIC_FUNCTION, ImageDescriptor.createFromURL(getBundle().getEntry("/icons/jdt/methpub_obj.gif")));
        reg.put(PRIVATE_FUNCTION,
                ImageDescriptor.createFromURL(getBundle().getEntry("/icons/jdt/methpri_obj.gif")));
        reg.put(CLASS, ImageDescriptor.createFromURL(getBundle().getEntry("/icons/jdt/class_obj.gif")));
        reg.put(SORT, ImageDescriptor.createFromURL(getBundle().getEntry("/icons/jdt/alphab_sort_co.gif")));
    }

    public static final String NS = "icon.namespace";
    public static final String PUBLIC_FUNCTION = "icon.function.public";
    public static final String PRIVATE_FUNCTION = "icon.function.private";
    public static final String CLASS = "class_obj.gif";
    public static final String SORT = "alphab_sort_co.gif";

    public static boolean isAutoReloadOnStartupSaveEnabled() {
        return CCWPlugin.getDefault().getPreferenceStore()
                .getBoolean(PreferenceConstants.CCW_GENERAL_AUTO_RELOAD_ON_STARTUP_SAVE);
    }

    public static boolean isAutoReloadOnStartupSaveEnabled(ILaunch launch) {
        return (Boolean.parseBoolean(launch.getAttribute(LaunchUtils.ATTR_IS_AUTO_RELOAD_ENABLED)));
    }

    public static REPLView[] getREPLViews() {
        final ArrayList<REPLView> ret = new ArrayList<REPLView>(5);

        DisplayUtil.syncExec(new Runnable() {
            public void run() {
                IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
                if (window != null) {
                    IWorkbenchPage page = window.getActivePage();
                    if (page != null) {
                        for (IViewReference r : page.getViewReferences()) {
                            IViewPart v = r.getView(false);
                            if (REPLView.class.isInstance(v)) {
                                ret.add((REPLView) v);
                            }
                        }
                    }
                }
            }
        });

        return ret.toArray(new REPLView[ret.size()]);
    }

    public static REPLView getProjectREPL(final IProject project) {
        REPLView[] repls = getREPLViews();

        for (REPLView replView : repls) {
            ILaunch launch = replView.getLaunch();
            if (launch != null && !launch.isTerminated()) {
                String launchProject = LaunchUtils.getProjectName(launch);
                if (launchProject != null && launchProject.equals(project.getName())) {
                    return replView;
                }
            }
        }
        ;

        return null;
    }

    public SafeConnection getProjectREPLSafeConnection(IProject project) {
        REPLView repl = getProjectREPL(project);
        return repl == null ? null : repl.getSafeToolingConnection();
    }

    private IScanContext scanContext;

    public synchronized IScanContext getDefaultScanContext() {
        if (scanContext == null) {
            scanContext = new StaticScanContext();
        }
        return scanContext;
    }

    public static RGB getPreferenceRGB(IPreferenceStore store, String preferenceKey, RGB defaultColor) {
        return store.getBoolean(SyntaxColoringHelper.getEnabledPreferenceKey(preferenceKey))
                ? PreferenceConverter.getColor(store, preferenceKey)
                : defaultColor;
    }

    /** 
     * Not thread safe, but should only be called from the UI Thread, so it's
     * not really a problem.
     * @param rgb
     * @return The <code>Color</code> instance cached for this rgb value, creating
     *         it along the way if required.
     */
    public static Color getColor(RGB rgb) {
        ColorRegistry r = getDefault().getColorCache();
        String rgbString = StringConverter.asString(rgb);
        if (!r.hasValueFor(rgbString)) {
            r.put(rgbString, rgb);
        }
        return r.get(rgbString);
    }

    public static Color getPreferenceColor(IPreferenceStore store, String preferenceKey, RGB defaultColor) {
        return getColor(getPreferenceRGB(store, preferenceKey, defaultColor));
    }

    public static void registerEditorColors(IPreferenceStore store, RGB foregroundColor) {
        final ColorRegistry colorCache = getDefault().getColorCache();

        for (Keyword token : PreferenceConstants.colorizableTokens) {
            PreferenceConstants.ColorizableToken tokenStyle = PreferenceConstants.getColorizableToken(store, token,
                    foregroundColor);
            colorCache.put(tokenStyle.rgb.toString(), tokenStyle.rgb);
        }

    }

    public static IWorkbenchPage getActivePage() {
        return getDefault().internalGetActivePage();
    }

    // copied from JavaPlugin
    private IWorkbenchPage internalGetActivePage() {
        IWorkbenchWindow window = getWorkbench().getActiveWorkbenchWindow();
        if (window == null)
            return null;
        return window.getActivePage();
    }
}