/** * (c) 2011, Alejandro Serrano * Released under the terms of the EPL. */ package net.sf.eclipsefp.haskell.browser; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Writer; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.MissingResourceException; import java.util.ResourceBundle; import net.sf.eclipsefp.haskell.browser.client.NullBrowserServer; import net.sf.eclipsefp.haskell.browser.client.StreamBrowserServer; import net.sf.eclipsefp.haskell.browser.items.Declaration; import net.sf.eclipsefp.haskell.browser.items.Documented; import net.sf.eclipsefp.haskell.browser.items.HoogleResult; import net.sf.eclipsefp.haskell.browser.items.Packaged; import net.sf.eclipsefp.haskell.browser.util.BrowserText; import net.sf.eclipsefp.haskell.util.FileUtil; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.statushandlers.StatusManager; import org.osgi.framework.BundleContext; /** * The activator class controls the plug-in life cycle */ public class BrowserPlugin extends AbstractUIPlugin implements IDatabaseLoadedListener, IHoogleLoadedListener { // The plug-in ID public static final String PLUGIN_ID = "net.sf.eclipsefp.haskell.browser"; //$NON-NLS-1$ public static final String BROWSER_VERSION = "0.2"; public static final String DIST_FOLDER = ".dist-scion-browser"; /** The plugin's resource bundle. */ private ResourceBundle resourceBundle; // The shared instance private static BrowserPlugin plugin; // The instance of the server used for communication private BrowserServer server; // The console log output private Writer logStream; // Listeners for loading new databases private ArrayList<IDatabaseLoadedListener> dbLoadedListeners; private ArrayList<IHoogleLoadedListener> hoogleLoadedListeners; /** * the sandbox used for user projects, useful to get packages to index, etc. */ private static String sandboxPath; /** * the sandbox used for internal tools, useful to get the Hoogle we downloaded. */ private static String toolSandboxPath; /** * The constructor */ public BrowserPlugin() { this.server = new NullBrowserServer(); this.logStream = null; this.dbLoadedListeners = new ArrayList<>(); this.hoogleLoadedListeners = new ArrayList<>(); } /* * (non-Javadoc) * * @see * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext * ) */ @Override public void start(BundleContext context) throws Exception { super.start(context); plugin = this; } /** * Get the plugin's resource bundle (aka "plugin.properties"). */ public ResourceBundle getResourceBundle() { if (resourceBundle == null) { try { resourceBundle = Platform.getResourceBundle( getBundle() ); } catch( MissingResourceException x ) { resourceBundle = null; } } return resourceBundle; } /* * (non-Javadoc) * * @see * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext * ) */ @Override public void stop(BundleContext context) throws Exception { plugin = null; if (server != null) server.stop(); super.stop(context); } public static String getStringResource(String key) { BrowserPlugin p = getDefault(); if (p != null) { try { return p.getResourceBundle().getString(key); } catch (MissingResourceException ex) { return key; } } else { // plugin still loading return key; } } /** * Returns the shared instance * * @return the shared instance */ public static BrowserPlugin getDefault() { return plugin; } /** * Returns an image descriptor for the image file at the given plug-in * relative path * * @param path * the path * @return the image descriptor */ public static ImageDescriptor getImageDescriptor(String path) { return imageDescriptorFromPlugin(PLUGIN_ID, path); } public BrowserServer getInstance() { return this.server; } /** * Return the shared instance of the browser server * That instance should be the only one in the workspace * * @return the shared instance */ public static BrowserServer getSharedInstance() { return getDefault().server; } /** * Sets the console where scion-browser output will be redirected. * * @param logStream Writer which will receive the info */ public void setLogStream(Writer logStream) { this.logStream = logStream; this.server.setLogStream(logStream); } /** * Static version of {@link BrowserPlugin#setLogStream(Writer)} * * @param logStream */ public static void setSharedLogStream(Writer logStream) { getDefault().setLogStream(logStream); } /** * log errors? * @param logError */ public static void setSharedLogError(boolean logError){ BrowserServer bs=getDefault().server; if (bs!=null){ bs.setLogError(logError); } } /** * Changes the scion-browser used to get information about packages * If the browser cannot be loaded, an empty browser will be used * * @param path file path to the server executable */ public void changeInstance(IPath path,boolean logError) { // Destroy previous scion-browser if (this.server != null) this.server.stop(); if (path.toFile().exists()) { try { this.server = new StreamBrowserServer(path,logError); this.server.addDatabaseLoadedListener(this); this.server.addHoogleLoadedListener(this); this.server.setLogStream(this.logStream); } catch (Throwable ex) { this.server = new NullBrowserServer(); } } else { this.server = new NullBrowserServer(); } } /** * Static version of {@link BrowserPlugin#changeInstance(IPath,boolean)} * * @param path */ public static void changeSharedInstance(IPath path,boolean logError) { getDefault().changeInstance(path,logError); } /** * Makes the plug-in to use a null browser, that is, a browser * which will return empty sets for any query */ public void useNullInstance() { // Destroy previous scion-browser if (this.server != null) this.server.stop(); this.server = new NullBrowserServer(); } /** * Static version of {@link BrowserPlugin#useNullInstance()} */ public static void useNullSharedInstance() { getDefault().useNullInstance(); } /** * Makes the plug-in to use the buil-in browser */ public static void useSharedBuiltinInstance(boolean logError) { IPath path = builtinServerExecutablePath(); changeSharedInstance(path,logError); } /** * Loads the local database in the current shared instance * * @param rebuild if true, new packages installed are added to the database * @return OK or ERROR */ public static IStatus loadLocalDatabase(boolean rebuild) { try { getSharedInstance().loadLocalDatabase(getLocalDatabasePath().toOSString(), rebuild); return Status.OK_STATUS; } catch (Throwable ex) { return new Status(IStatus.ERROR, PLUGIN_ID, BrowserText.error_loadlocaldb, ex); } } public static IStatus initHoogle(boolean addToDB) { try { getSharedInstance().initHoogle(getLocalDatabasePath().toOSString(),addToDB); return Status.OK_STATUS; } catch (Throwable ex) { return new Status(IStatus.ERROR, PLUGIN_ID, BrowserText.error_loadlocaldb, ex); } } public static HoogleResult[] queryHoogle(Database db,String query) throws Exception{ return getSharedInstance().queryHoogle(db,getLocalDatabasePath().toOSString(),query); } /** * Loads the Hackage database in the current shared instance * * @param rebuild if true, Hoogle data is downloaded and the database is rebuilt * @return OK or ERROR */ public static IStatus loadHackageDatabase(boolean rebuild) { try { getSharedInstance().loadHackageDatabase(getHackageDatabasePath().toOSString(), rebuild); return Status.OK_STATUS; } catch (Throwable ex) { return new Status(IStatus.ERROR, PLUGIN_ID, BrowserText.error_loadhackagedb, ex); } } /** * Returns the path to the place where databases are saved * If it does not exists, the directory is created * * @return the path to the directory */ public static IPath builtinBrowserDirectoryPath() { IPath path = getDefault().getStateLocation().append( "scion-browser-".concat(BROWSER_VERSION).concat("-dbs")); //$NON-NLS-1$ if (!path.toFile().exists()) path.toFile().mkdirs(); return path; } /** * Returns the path to the local database * * @return the path */ public static IPath getLocalDatabasePath() { return builtinBrowserDirectoryPath().append("local.db"); } /** * Returns the path to the Hackage database * * @return the path */ public static IPath getHackageDatabasePath() { return builtinBrowserDirectoryPath().append("hackage.db"); } /** * Adds a new listener for database changes * * @param listener */ public void addDatabaseLoadedListener(IDatabaseLoadedListener listener) { dbLoadedListeners.add(listener); } /** * Adds a new listener for Hoogle changes * * @param listener */ public void addHoogleLoadedListener(IHoogleLoadedListener listener) { hoogleLoadedListeners.add(listener); } /** * Raises an event for all listeners currently registered * * @param e */ protected void notifyDatabaseLoaded(DatabaseLoadedEvent e) { for (IDatabaseLoadedListener listener : dbLoadedListeners) listener.databaseLoaded(e); } /** * Raises an event for all listeners currently registered * * @param e */ protected void notifyDatabaseUnloaded(BrowserEvent e) { for (IDatabaseLoadedListener listener : dbLoadedListeners) listener.databaseUnloaded(e); } /** * Raises an event for all listeners currently registered * * @param e */ protected void notifyHoogleLoaded(BrowserEvent e) { for (IHoogleLoadedListener listener : hoogleLoadedListeners) listener.hoogleLoaded(e); } /** * Raises an event for all listeners currently registered * * @param e */ protected void notifyHoogleUnloaded(BrowserEvent e) { for (IHoogleLoadedListener listener : hoogleLoadedListeners) listener.hoogleUnloaded(e); } @Override public void databaseLoaded(DatabaseLoadedEvent e) { notifyDatabaseLoaded(e); } @Override public void databaseUnloaded(BrowserEvent e) { notifyDatabaseUnloaded(e); } @Override public void hoogleLoaded(BrowserEvent e) { notifyHoogleLoaded(e); } @Override public void hoogleUnloaded(BrowserEvent e) { notifyHoogleUnloaded(e); } public boolean isAnyDatabaseLoaded() { if (this.server == null) return false; return this.server.isAnyDatabaseLoaded(); } public boolean isLocalDatabaseLoaded() { if (this.server == null) return false; return this.server.isLocalDatabaseLoaded(); } public boolean isHackageDatabaseLoaded() { if (this.server == null) return false; return this.server.isHackageDatabaseLoaded(); } public boolean isHoogleLoaded() { if (this.server == null) return false; return this.server.isHoogleLoaded(); } /** * Generate the built-in Scion server's build area directory path. * * @return An IPath to the build area subdirectory off the workspace's state * location. */ public static IPath builtinServerDirectoryPath() { return getDefault().getStateLocation().append("scion-browser-".concat(BROWSER_VERSION)); } /** * Open the Scion server's zip archive as an input stream. * * @return The InputStream. */ @Deprecated public static InputStream builinServerArchive() { String zipArchive = "scion-browser-" + BROWSER_VERSION + ".zip"; //$NON-NLS-1$ //$NON-NLS-2$ return BrowserPlugin.class.getResourceAsStream(zipArchive); } /** * Generate the built-in server's executable path, where we'd expect to find * it relative to the build directory. Note that destDir is most likely the * same as what {@link #builtinServerDirectoryPath * builtinServerDirectoryPath} generated. * * @param destDir * The destination directory where the built-in scion server is * being built. */ public static IPath serverExecutablePath(IPath destDir) { IPath exePath = destDir.append("dist").append("build").append("scion-browser"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return exePath.append(FileUtil.makeExecutableName("scion-browser")); } /** * Generate the built-in server's executable path in the default state * location's directory. * * @return An IPath to the built-in server's executable. */ public static IPath builtinServerExecutablePath() { return serverExecutablePath(builtinServerDirectoryPath()); } public static void logError(String message, Throwable cause) { log(IStatus.ERROR, message, cause); } public static void log(int severity, String message, Throwable cause) { Status status = new Status(severity, BrowserPlugin.PLUGIN_ID, severity, message, cause); logStatus(status); } public static void logStatus(IStatus status) { StatusManager.getManager().handle(status); } /** * get Documentation from Documented. This tries a bit harder to get documentation from the Browser info * @param d * @return */ public static String getDoc(Documented d){ if (d == null){ return ""; } String s=d.getDoc(); if (s!=null && s.length()>0){ return s; } if (d instanceof Declaration){ Declaration decl=(Declaration)d; return getDoc(decl.getModule().toString(),decl.getName(),d); } return ""; } /** * get Documentation from Documented (maybe), knowing the module and name. This tries a bit harder to get documentation from the Browser info * @param module * @param name * @param d * @return */ public static String getDoc(String module, String name, Documented d){ if (module!=null && module.length()>0){ if (d==null || d.getDoc()==null || d.getDoc().length()==0){ try { Packaged<Declaration>[] decls=BrowserPlugin.getSharedInstance().getDeclarations( Database.LOCAL, module ); for (Packaged<Declaration> p:decls){ if (p.getElement().getName().equals( name )){ d=p.getElement(); break; } } } catch (Exception e){ logError(e.getLocalizedMessage(), e ); } } if ((d==null || d.getDoc()==null || d.getDoc().length()==0) && module.startsWith( "GHC" )){ try { Packaged<Declaration>[] decls=BrowserPlugin.getSharedInstance().getDeclarations( Database.LOCAL, "Prelude" ); for (Packaged<Declaration> p:decls){ if (p.getElement().getName().equals( name )){ d=p.getElement(); break; } } } catch (Exception e){ logError(e.getLocalizedMessage(), e ); } } } if (d!=null){ return d.getDoc(); } return ""; } public static String getToolSandboxPath() { return toolSandboxPath; } public static void setToolSandboxPath(String sandboxPath) { BrowserPlugin.toolSandboxPath = sandboxPath; } public static String getSandboxPath() { return sandboxPath; } public static void setSandboxPath(String sandboxPath) { BrowserPlugin.sandboxPath = sandboxPath; } public static void addToHoogle(File f){ File db=getLocalDatabasePath().toFile(); File txt=new File(new File(db.getParentFile(),"hoogle"),f.getName()); try { Files.copy(f.toPath(), txt.toPath(), StandardCopyOption.REPLACE_EXISTING); initHoogle(true); } catch (IOException ioe){ BrowserPlugin.logError(ioe.getLocalizedMessage(), ioe); } } }