Java tutorial
/******************************************************************************* * 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(); } }