alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * PluginJAR.java - Controls JAR loading and unloading
 * :tabSize=8:indentSize=8:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 1999, 2004 Slava Pestov
 *
 * 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 2
 * of the License, or 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package org.gjt.sp.jedit;

//{{{ Imports
import javax.swing.SwingUtilities;
import java.io.*;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;
import java.util.zip.*;
import org.gjt.sp.jedit.browser.VFSBrowser;
import org.gjt.sp.jedit.buffer.*;
import org.gjt.sp.jedit.gui.DockableWindowManager;
import org.gjt.sp.jedit.msg.*;
import org.gjt.sp.util.Log;
//}}}

/**
 * Loads and unloads plugins.

* *

JAR file contents

* * When loading a plugin, jEdit looks for the following resources: * *
    *
  • A file named actions.xml defining plugin actions. * Only one such file per plugin is allowed. See {@link ActionSet} for * syntax.
  • *
  • A file named browser.actions.xml defining file system * browser actions. * Only one such file per plugin is allowed. See {@link ActionSet} for * syntax.
  • *
  • A file named dockables.xml defining dockable windows. * Only one such file per plugin is allowed. See {@link * org.gjt.sp.jedit.gui.DockableWindowManager} for * syntax.
  • *
  • A file named services.xml defining additional services * offered by the plugin, such as virtual file systems. * Only one such file per plugin is allowed. See {@link * org.gjt.sp.jedit.ServiceManager} for * syntax.
  • *
  • File with extension .props containing name/value pairs * separated by an equals sign. * A plugin can supply any number of property files. Property files are used * to define plugin men items, plugin option panes, as well as arbitriary * settings and strings used by the plugin. See {@link EditPlugin} for * information about properties used by jEdit. See * java.util.Properties for property file syntax.
  • *
* * For a plugin to actually do something once it is resident in memory, * it must contain a class whose name ends with Plugin. * This class, known as the plugin core class must extend * {@link EditPlugin} and define a few required properties, otherwise it is * ignored. * *

Dynamic and deferred loading

* * Unlike in prior jEdit versions, jEdit 4.2 and later allow * plugins to be added and removed to the resident set at any time using * the {@link jEdit#addPluginJAR(String)} and * {@link jEdit#removePluginJAR(PluginJAR,boolean)} methods. Furthermore, the * plugin core class might not be loaded until the plugin is first used. See * {@link EditPlugin#start()} for a full description. * * @see org.gjt.sp.jedit.jEdit#getProperty(String) * @see org.gjt.sp.jedit.jEdit#getPlugin(String) * @see org.gjt.sp.jedit.jEdit#getPlugins() * @see org.gjt.sp.jedit.jEdit#getPluginJAR(String) * @see org.gjt.sp.jedit.jEdit#getPluginJARs() * @see org.gjt.sp.jedit.jEdit#addPluginJAR(String) * @see org.gjt.sp.jedit.jEdit#removePluginJAR(PluginJAR,boolean) * @see org.gjt.sp.jedit.ActionSet * @see org.gjt.sp.jedit.gui.DockableWindowManager * @see org.gjt.sp.jedit.OptionPane * @see org.gjt.sp.jedit.PluginJAR * @see org.gjt.sp.jedit.ServiceManager * * @author Slava Pestov * @version $Id: PluginJAR.java,v 1.47 2004/04/20 19:58:39 spestov Exp $ * @since jEdit 4.2pre1 */ public class PluginJAR { //{{{ getPath() method /** * Returns the full path name of this plugin's JAR file. */ public String getPath() { return path; } //}}} //{{{ getCachePath() method /** * Returns the full path name of this plugin's summary file. * The summary file is used to store certain information which allows * loading of the plugin's resources and core class to be deferred * until the plugin is first used. As long as a plugin is using the * jEdit 4.2 plugin API, no extra effort is required to take advantage * of the summary cache. */ public String getCachePath() { return cachePath; } //}}} //{{{ getFile() method /** * Returns a file pointing to the plugin JAR. */ public File getFile() { return file; } //}}} //{{{ getClassLoader() method /** * Returns the plugin's class loader. */ public JARClassLoader getClassLoader() { return classLoader; } //}}} //{{{ getZipFile() method /** * Returns the plugin's JAR file, opening it if necessary. * @since jEdit 4.2pre1 */ public synchronized ZipFile getZipFile() throws IOException { if(zipFile == null) { Log.log(Log.DEBUG,this,"Opening " + path); zipFile = new ZipFile(path); } return zipFile; } //}}} //{{{ getActions() method /** * @deprecated Call getActionSet() instead */ public ActionSet getActions() { return getActionSet(); } //}}} //{{{ getActionSet() method /** * Returns the plugin's action set for the jEdit action context * {@link jEdit#getActionContext()}. These actions are loaded from * the actions.xml file; see {@link ActionSet}. *. * @since jEdit 4.2pre1 */ public ActionSet getActionSet() { return actions; } //}}} //{{{ getBrowserActionSet() method /** * Returns the plugin's action set for the file system browser action * context {@link * org.gjt.sp.jedit.browser.VFSBrowser#getActionContext()}. * These actions are loaded from * the browser.actions.xml file; see {@link ActionSet}. *. * @since jEdit 4.2pre1 */ public ActionSet getBrowserActionSet() { return browserActions; } //}}} //{{{ checkDependencies() method /** * Returns true if all dependencies are satisified, false otherwise. * Also if dependencies are not satisfied, the plugin is marked as * "broken". */ public boolean checkDependencies() { if(plugin == null) return true; int i = 0; boolean ok = true; boolean optional = false; String name = plugin.getClassName(); String dep; while((dep = jEdit.getProperty("plugin." + name + ".depend." + i++)) != null) { if(dep.startsWith("optional ")) { optional = true; dep = dep.substring("optional ".length()); } int index = dep.indexOf(' '); if(index == -1) { Log.log(Log.ERROR,this,name + " has an invalid" + " dependency: " + dep); ok = false; continue; } String what = dep.substring(0,index); String arg = dep.substring(index + 1); if(what.equals("jdk")) { if(!optional && MiscUtilities.compareStrings( System.getProperty("java.version"), arg,false) < 0) { String[] args = { arg, System.getProperty("java.version") }; jEdit.pluginError(path,"plugin-error.dep-jdk",args); ok = false; } } else if(what.equals("jedit")) { if(arg.length() != 11) { Log.log(Log.ERROR,this,"Invalid jEdit version" + " number: " + arg); ok = false; } if(!optional && MiscUtilities.compareStrings( jEdit.getBuild(),arg,false) < 0) { String needs = MiscUtilities.buildToVersion(arg); String[] args = { needs, jEdit.getVersion() }; jEdit.pluginError(path, "plugin-error.dep-jedit",args); ok = false; } } else if(what.equals("plugin")) { int index2 = arg.indexOf(' '); if(index2 == -1) { Log.log(Log.ERROR,this,name + " has an invalid dependency: " + dep + " (version is missing)"); ok = false; continue; } String pluginName = arg.substring(0,index2); String needVersion = arg.substring(index2 + 1); String currVersion = jEdit.getProperty("plugin." + pluginName + ".version"); EditPlugin plugin = jEdit.getPlugin(pluginName); if(plugin == null) { if(!optional) { String[] args = { needVersion, pluginName }; jEdit.pluginError(path, "plugin-error.dep-plugin.no-version", args); ok = false; } } else if(MiscUtilities.compareStrings( currVersion,needVersion,false) < 0) { if(!optional) { String[] args = { needVersion, pluginName, currVersion }; jEdit.pluginError(path, "plugin-error.dep-plugin",args); ok = false; } } else if(plugin instanceof EditPlugin.Broken) { if(!optional) { String[] args = { pluginName }; jEdit.pluginError(path, "plugin-error.dep-plugin.broken",args); ok = false; } } else { PluginJAR jar = plugin.getPluginJAR(); jar.theseRequireMe.add(path); weRequireThese.add(jar.getPath()); } } else if(what.equals("class")) { if(!optional) { try { classLoader.loadClass(arg,false); } catch(Exception e) { String[] args = { arg }; jEdit.pluginError(path, "plugin-error.dep-class",args); ok = false; } } } else { Log.log(Log.ERROR,this,name + " has unknown" + " dependency: " + dep); ok = false; } } // each JAR file listed in the plugin's jars property // needs to know that we need them String jars = jEdit.getProperty("plugin." + plugin.getClassName() + ".jars"); if(jars != null) { String dir = MiscUtilities.getParentOfPath(path); StringTokenizer st = new StringTokenizer(jars); while(st.hasMoreTokens()) { String jarPath = MiscUtilities.constructPath( dir,st.nextToken()); PluginJAR jar = jEdit.getPluginJAR(jarPath); if(jar == null) { String[] args = { jarPath }; jEdit.pluginError(path, "plugin-error.missing-jar",args); ok = false; } else { weRequireThese.add(jarPath); jar.theseRequireMe.add(path); } } } if(!ok) breakPlugin(); return ok; } //}}} //{{{ getDependentPlugins() method /** * Returns an array of all plugins that depend on this one. * @since jEdit 4.2pre2 */ public String[] getDependentPlugins() { return (String[])theseRequireMe.toArray( new String[theseRequireMe.size()]); } //}}} //{{{ getPlugin() method /** * Returns the plugin core class for this JAR file. Note that if the * plugin has not been activated, this will return an instance of * {@link EditPlugin.Deferred}. If you need the actual plugin core * class instance, call {@link #activatePlugin()} first. * * @since jEdit 4.2pre1 */ public EditPlugin getPlugin() { return plugin; } //}}} //{{{ activatePlugin() method /** * Loads the plugin core class. Does nothing if the plugin core class * has already been loaded. This method might be called on startup, * depending on what properties are set. See {@link EditPlugin#start()}. * This method is thread-safe. * * @since jEdit 4.2pre1 */ public void activatePlugin() { synchronized(this) { if(activated) { // recursive call return; } activated = true; } if(!(plugin instanceof EditPlugin.Deferred)) return; String className = plugin.getClassName(); try { Class clazz = classLoader.loadClass(className,false); int modifiers = clazz.getModifiers(); if(Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers) || !EditPlugin.class.isAssignableFrom(clazz)) { Log.log(Log.ERROR,this,"Plugin has properties but does not extend EditPlugin: " + className); breakPlugin(); return; } plugin = (EditPlugin)clazz.newInstance(); plugin.jar = (EditPlugin.JAR)this; } catch(Throwable t) { breakPlugin(); Log.log(Log.ERROR,this,"Error while starting plugin " + className); Log.log(Log.ERROR,this,t); String[] args = { t.toString() }; jEdit.pluginError(path,"plugin-error.start-error",args); return; } if(jEdit.isMainThread() || SwingUtilities.isEventDispatchThread()) { startPlugin(); } else { // for thread safety startPluginLater(); } EditBus.send(new PluginUpdate(this,PluginUpdate.ACTIVATED,false)); } //}}} //{{{ activateIfNecessary() method /** * Should be called after a new plugin is installed. * @since jEdit 4.2pre2 */ public void activatePluginIfNecessary() { if(!(plugin instanceof EditPlugin.Deferred && plugin != null)) return; String className = plugin.getClassName(); // default for plugins that don't specify this property (ie, // 4.1-style plugins) is to load them on startup String activate = jEdit.getProperty("plugin." + className + ".activate"); if(activate == null) { // 4.1 plugin if(!jEdit.isMainThread()) { breakPlugin(); jEdit.pluginError(path,"plugin-error.not-42",null); } else activatePlugin(); } else { // 4.2 plugin // if at least one property listed here is true, // load the plugin boolean load = false; StringTokenizer st = new StringTokenizer(activate); while(st.hasMoreTokens()) { String prop = st.nextToken(); boolean value = jEdit.getBooleanProperty(prop); if(value) { Log.log(Log.DEBUG,this,"Activating " + className + " because of " + prop); load = true; break; } } if(load) activatePlugin(); } } //}}} //{{{ deactivatePlugin() method /** * Unloads the plugin core class. Does nothing if the plugin core class * has not been loaded. * This method can only be called from the AWT event dispatch thread! * @see EditPlugin#stop() * * @since jEdit 4.2pre3 */ public void deactivatePlugin(boolean exit) { if(!activated) return; if(!exit) { // buffers retain a reference to the fold handler in // question... and the easiest way to handle fold // handler unloading is this... Buffer buffer = jEdit.getFirstBuffer(); while(buffer != null) { if(buffer.getFoldHandler() != null && buffer.getFoldHandler().getClass() .getClassLoader() == classLoader) { buffer.setFoldHandler( new DummyFoldHandler()); } buffer = buffer.getNext(); } } if(plugin != null && !(plugin instanceof EditPlugin.Broken)) { if(plugin instanceof EBPlugin) EditBus.removeFromBus((EBPlugin)plugin); try { plugin.stop(); } catch(Throwable t) { Log.log(Log.ERROR,this,"Error while " + "stopping plugin:"); Log.log(Log.ERROR,this,t); } plugin = new EditPlugin.Deferred( plugin.getClassName()); plugin.jar = (EditPlugin.JAR)this; EditBus.send(new PluginUpdate(this, PluginUpdate.DEACTIVATED,exit)); if(!exit) { // see if this is a 4.1-style plugin String activate = jEdit.getProperty("plugin." + plugin.getClassName() + ".activate"); if(activate == null) { breakPlugin(); jEdit.pluginError(path,"plugin-error.not-42",null); } } } activated = false; } //}}} //{{{ getDockablesURI() method /** * Returns the location of the plugin's * dockables.xml file. * @since jEdit 4.2pre1 */ public URL getDockablesURI() { return dockablesURI; } //}}} //{{{ getServicesURI() method /** * Returns the location of the plugin's * services.xml file. * @since jEdit 4.2pre1 */ public URL getServicesURI() { return servicesURI; } //}}} //{{{ toString() method public String toString() { if(plugin == null) return path; else return path + ",class=" + plugin.getClassName(); } //}}} //{{{ Package-private members //{{{ Static methods //{{{ getPluginCache() method static PluginCacheEntry getPluginCache(PluginJAR plugin) { String jarCachePath = plugin.getCachePath(); if(jarCachePath == null) return null; DataInputStream din = null; try { PluginCacheEntry cache = new PluginCacheEntry(); cache.plugin = plugin; cache.modTime = plugin.getFile().lastModified(); din = new DataInputStream( new BufferedInputStream( new FileInputStream(jarCachePath))); if(cache.read(din)) return cache; else { // returns false with outdated cache return null; } } catch(FileNotFoundException fnf) { return null; } catch(IOException io) { Log.log(Log.ERROR,PluginJAR.class,io); return null; } finally { try { if(din != null) din.close(); } catch(IOException io) { Log.log(Log.ERROR,PluginJAR.class,io); } } } //}}} //{{{ setPluginCache() method static void setPluginCache(PluginJAR plugin, PluginCacheEntry cache) { String jarCachePath = plugin.getCachePath(); if(jarCachePath == null) return; Log.log(Log.DEBUG,PluginJAR.class,"Writing " + jarCachePath); DataOutputStream dout = null; try { dout = new DataOutputStream( new BufferedOutputStream( new FileOutputStream(jarCachePath))); cache.write(dout); dout.close(); } catch(IOException io) { Log.log(Log.ERROR,PluginJAR.class,io); try { if(dout != null) dout.close(); } catch(IOException io2) { Log.log(Log.ERROR,PluginJAR.class,io2); } new File(jarCachePath).delete(); } } //}}} //}}} //{{{ PluginJAR constructor PluginJAR(File file) { this.path = file.getPath(); String jarCacheDir = jEdit.getJARCacheDirectory(); if(jarCacheDir != null) { cachePath = MiscUtilities.constructPath( jarCacheDir,file.getName() + ".summary"); } this.file = file; classLoader = new JARClassLoader(this); actions = new ActionSet(); } //}}} //{{{ init() method void init() { boolean initialized = false; PluginCacheEntry cache = getPluginCache(this); if(cache != null) { loadCache(cache); classLoader.activate(); initialized = true; } else { try { cache = generateCache(); if(cache != null) { setPluginCache(this,cache); classLoader.activate(); initialized = true; } } catch(IOException io) { Log.log(Log.ERROR,this,"Cannot load" + " plugin " + path); Log.log(Log.ERROR,this,io); String[] args = { io.toString() }; jEdit.pluginError(path,"plugin-error.load-error",args); uninit(false); } } } //}}} //{{{ uninit() method void uninit(boolean exit) { deactivatePlugin(exit); if(!exit) { Iterator iter = weRequireThese.iterator(); while(iter.hasNext()) { String path = (String)iter.next(); PluginJAR jar = jEdit.getPluginJAR(path); if(jar != null) jar.theseRequireMe.remove(this.path); } classLoader.deactivate(); BeanShell.resetClassManager(); if(actions != null) jEdit.getActionContext().removeActionSet(actions); if(browserActions != null) VFSBrowser.getActionContext().removeActionSet(browserActions); DockableWindowManager.unloadDockableWindows(this); ServiceManager.unloadServices(this); jEdit.removePluginProps(properties); try { if(zipFile != null) { zipFile.close(); zipFile = null; } } catch(IOException io) { Log.log(Log.ERROR,this,io); } } } //}}} //{{{ getClasses() method String[] getClasses() { return classes; } //}}} //}}} //{{{ Private members //{{{ Instance variables private String path; private String cachePath; private File file; private JARClassLoader classLoader; private ZipFile zipFile; private Properties properties; private String[] classes; private ActionSet actions; private ActionSet browserActions; private EditPlugin plugin; private URL dockablesURI; private URL servicesURI; private boolean activated; private List theseRequireMe = new LinkedList(); private List weRequireThese = new LinkedList(); //}}} //{{{ actionsPresentButNotCoreClass() method private void actionsPresentButNotCoreClass() { Log.log(Log.WARNING,this,getPath() + " has an actions.xml but no plugin core class"); actions.setLabel("MISSING PLUGIN CORE CLASS"); } //}}} //{{{ loadCache() method private void loadCache(PluginCacheEntry cache) { classes = cache.classes; /* this should be before dockables are initialized */ if(cache.cachedProperties != null) { properties = cache.cachedProperties; jEdit.addPluginProps(cache.cachedProperties); } if(cache.actionsURI != null && cache.cachedActionNames != null) { actions = new ActionSet(this, cache.cachedActionNames, cache.cachedActionToggleFlags, cache.actionsURI); } if(cache.browserActionsURI != null && cache.cachedBrowserActionNames != null) { browserActions = new ActionSet(this, cache.cachedBrowserActionNames, cache.cachedBrowserActionToggleFlags, cache.browserActionsURI); VFSBrowser.getActionContext().addActionSet(browserActions); } if(cache.dockablesURI != null && cache.cachedDockableNames != null && cache.cachedDockableActionFlags != null) { dockablesURI = cache.dockablesURI; DockableWindowManager.cacheDockableWindows(this, cache.cachedDockableNames, cache.cachedDockableActionFlags); } if(actions.size() != 0) jEdit.addActionSet(actions); if(cache.servicesURI != null && cache.cachedServices != null) { servicesURI = cache.servicesURI; for(int i = 0; i < cache.cachedServices.length; i++) { ServiceManager.Descriptor d = cache.cachedServices[i]; ServiceManager.registerService(d); } } if(cache.pluginClass != null) { // Check if a plugin with the same name // is already loaded if(jEdit.getPlugin(cache.pluginClass) != null) { jEdit.pluginError(path, "plugin-error.already-loaded", null); uninit(false); } else { String label = jEdit.getProperty( "plugin." + cache.pluginClass + ".name"); actions.setLabel(jEdit.getProperty( "action-set.plugin", new String[] { label })); plugin = new EditPlugin.Deferred( cache.pluginClass); plugin.jar = (EditPlugin.JAR)this; } } else { if(actions.size() != 0) actionsPresentButNotCoreClass(); } } //}}} //{{{ generateCache() method private PluginCacheEntry generateCache() throws IOException { properties = new Properties(); LinkedList classes = new LinkedList(); ZipFile zipFile = getZipFile(); List plugins = new LinkedList(); PluginCacheEntry cache = new PluginCacheEntry(); cache.modTime = file.lastModified(); cache.cachedProperties = new Properties(); Enumeration entries = zipFile.entries(); while(entries.hasMoreElements()) { ZipEntry entry = (ZipEntry) entries.nextElement(); String name = entry.getName(); String lname = name.toLowerCase(); if(lname.equals("actions.xml")) { cache.actionsURI = classLoader.getResource(name); } else if(lname.equals("browser.actions.xml")) { cache.browserActionsURI = classLoader.getResource(name); } else if(lname.equals("dockables.xml")) { dockablesURI = classLoader.getResource(name); cache.dockablesURI = dockablesURI; } else if(lname.equals("services.xml")) { servicesURI = classLoader.getResource(name); cache.servicesURI = servicesURI; } else if(lname.endsWith(".props")) { InputStream in = classLoader.getResourceAsStream(name); properties.load(in); in.close(); } else if(name.endsWith(".class")) { String className = MiscUtilities .fileToClass(name); if(className.endsWith("Plugin")) { plugins.add(className); } classes.add(className); } } cache.cachedProperties = properties; jEdit.addPluginProps(properties); this.classes = cache.classes = (String[])classes.toArray( new String[classes.size()]); String label = null; Iterator iter = plugins.iterator(); while(iter.hasNext()) { String className = (String)iter.next(); String _label = jEdit.getProperty("plugin." + className + ".name"); String version = jEdit.getProperty("plugin." + className + ".version"); if(_label == null || version == null) { Log.log(Log.WARNING,this,"Ignoring: " + className); } else { cache.pluginClass = className; // Check if a plugin with the same name // is already loaded if(jEdit.getPlugin(className) != null) { jEdit.pluginError(path, "plugin-error.already-loaded", null); return null; } else { plugin = new EditPlugin.Deferred( className); plugin.jar = (EditPlugin.JAR)this; label = _label; } break; } } if(cache.actionsURI != null) { actions = new ActionSet(this,null,null, cache.actionsURI); actions.load(); cache.cachedActionNames = actions.getCacheableActionNames(); cache.cachedActionToggleFlags = new boolean[ cache.cachedActionNames.length]; for(int i = 0; i < cache.cachedActionNames.length; i++) { cache.cachedActionToggleFlags[i] = jEdit.getBooleanProperty( cache.cachedActionNames[i] + ".toggle"); } } if(cache.browserActionsURI != null) { browserActions = new ActionSet(this,null,null, cache.browserActionsURI); browserActions.load(); VFSBrowser.getActionContext().addActionSet(browserActions); cache.cachedBrowserActionNames = browserActions.getCacheableActionNames(); cache.cachedBrowserActionToggleFlags = new boolean[ cache.cachedBrowserActionNames.length]; for(int i = 0; i < cache.cachedBrowserActionNames.length; i++) { cache.cachedBrowserActionToggleFlags[i] = jEdit.getBooleanProperty( cache.cachedBrowserActionNames[i] + ".toggle"); } } if(dockablesURI != null) { DockableWindowManager.loadDockableWindows(this, dockablesURI,cache); } if(actions.size() != 0) { if(label != null) { actions.setLabel(jEdit.getProperty( "action-set.plugin", new String[] { label })); } else actionsPresentButNotCoreClass(); jEdit.addActionSet(actions); } if(servicesURI != null) { ServiceManager.loadServices(this,servicesURI,cache); } return cache; } //}}} //{{{ startPlugin() method private void startPlugin() { try { plugin.start(); } catch(Throwable t) { breakPlugin(); Log.log(Log.ERROR,PluginJAR.this, "Error while starting plugin " + plugin.getClassName()); Log.log(Log.ERROR,PluginJAR.this,t); String[] args = { t.toString() }; jEdit.pluginError(path,"plugin-error.start-error",args); } if(plugin instanceof EBPlugin) { if(jEdit.getProperty("plugin." + plugin.getClassName() + ".activate") == null) { // old plugins expected jEdit 4.1-style // behavior, where a PropertiesChanged // was sent after plugins were started ((EBComponent)plugin).handleMessage( new org.gjt.sp.jedit.msg.PropertiesChanged(null)); } EditBus.addToBus((EBPlugin)plugin); } // buffers retain a reference to the fold handler in // question... and the easiest way to handle fold // handler loading is this... Buffer buffer = jEdit.getFirstBuffer(); while(buffer != null) { FoldHandler handler = FoldHandler.getFoldHandler( buffer.getStringProperty("folding")); // == null before loaded if(buffer.getFoldHandler() != null && handler != null && handler != buffer.getFoldHandler()) { buffer.setFoldHandler(handler); } buffer = buffer.getNext(); } } //}}} //{{{ startPluginLater() method private void startPluginLater() { SwingUtilities.invokeLater(new Runnable() { public void run() { if(!activated) return; startPlugin(); } }); } //}}} //{{{ breakPlugin() method private void breakPlugin() { plugin = new EditPlugin.Broken(plugin.getClassName()); plugin.jar = (EditPlugin.JAR)this; // remove action sets, dockables, etc so that user doesn't // see the broken plugin uninit(false); // but we want properties to hang around jEdit.addPluginProps(properties); } //}}} //}}} //{{{ PluginCacheEntry class /** * Used by the DockableWindowManager and * ServiceManager to handle caching. * @since jEdit 4.2pre1 */ public static class PluginCacheEntry { public static final int MAGIC = 0xB7A2E420; //{{{ Instance variables public PluginJAR plugin; public long modTime; public String[] classes; public URL actionsURI; public String[] cachedActionNames; public boolean[] cachedActionToggleFlags; public URL browserActionsURI; public String[] cachedBrowserActionNames; public boolean[] cachedBrowserActionToggleFlags; public URL dockablesURI; public String[] cachedDockableNames; public boolean[] cachedDockableActionFlags; public URL servicesURI; public ServiceManager.Descriptor[] cachedServices; public Properties cachedProperties; public String pluginClass; //}}} /* read() and write() must be kept perfectly in sync... * its a very simple file format. doing it this way is * faster than serializing since serialization calls * reflection, etc. */ //{{{ read() method public boolean read(DataInputStream din) throws IOException { int cacheMagic = din.readInt(); if(cacheMagic != MAGIC) return false; String cacheBuild = readString(din); if(!cacheBuild.equals(jEdit.getBuild())) return false; long cacheModTime = din.readLong(); if(cacheModTime != modTime) return false; actionsURI = readURI(din); cachedActionNames = readStringArray(din); cachedActionToggleFlags = readBooleanArray(din); browserActionsURI = readURI(din); cachedBrowserActionNames = readStringArray(din); cachedBrowserActionToggleFlags = readBooleanArray(din); dockablesURI = readURI(din); cachedDockableNames = readStringArray(din); cachedDockableActionFlags = readBooleanArray(din); servicesURI = readURI(din); int len = din.readInt(); if(len == 0) cachedServices = null; else { cachedServices = new ServiceManager.Descriptor[len]; for(int i = 0; i < len; i++) { ServiceManager.Descriptor d = new ServiceManager.Descriptor( readString(din), readString(din), null, plugin); cachedServices[i] = d; } } classes = readStringArray(din); cachedProperties = readMap(din); pluginClass = readString(din); return true; } //}}} //{{{ write() method public void write(DataOutputStream dout) throws IOException { dout.writeInt(MAGIC); writeString(dout,jEdit.getBuild()); dout.writeLong(modTime); writeString(dout,actionsURI); writeStringArray(dout,cachedActionNames); writeBooleanArray(dout,cachedActionToggleFlags); writeString(dout,browserActionsURI); writeStringArray(dout,cachedBrowserActionNames); writeBooleanArray(dout,cachedBrowserActionToggleFlags); writeString(dout,dockablesURI); writeStringArray(dout,cachedDockableNames); writeBooleanArray(dout,cachedDockableActionFlags); writeString(dout,servicesURI); if(cachedServices == null) dout.writeInt(0); else { dout.writeInt(cachedServices.length); for(int i = 0; i < cachedServices.length; i++) { writeString(dout,cachedServices[i].clazz); writeString(dout,cachedServices[i].name); } } writeStringArray(dout,classes); writeMap(dout,cachedProperties); writeString(dout,pluginClass); } //}}} //{{{ Private members //{{{ readString() method private String readString(DataInputStream din) throws IOException { int len = din.readInt(); if(len == 0) return null; char[] str = new char[len]; for(int i = 0; i < len; i++) str[i] = din.readChar(); return new String(str); } //}}} //{{{ readURI() method private URL readURI(DataInputStream din) throws IOException { String str = readString(din); if(str == null) return null; else return new URL(str); } //}}} //{{{ readStringArray() method private String[] readStringArray(DataInputStream din) throws IOException { int len = din.readInt(); if(len == 0) return null; String[] str = new String[len]; for(int i = 0; i < len; i++) { str[i] = readString(din); } return str; } //}}} //{{{ readBooleanArray() method private boolean[] readBooleanArray(DataInputStream din) throws IOException { int len = din.readInt(); if(len == 0) return null; boolean[] bools = new boolean[len]; for(int i = 0; i < len; i++) { bools[i] = din.readBoolean(); } return bools; } //}}} //{{{ readMap() method private Properties readMap(DataInputStream din) throws IOException { Properties returnValue = new Properties(); int count = din.readInt(); for(int i = 0; i < count; i++) { String key = readString(din); String value = readString(din); if(value == null) value = ""; returnValue.put(key,value); } return returnValue; } //}}} //{{{ writeString() method private void writeString(DataOutputStream dout, Object obj) throws IOException { if(obj == null) { dout.writeInt(0); } else { String str = obj.toString(); dout.writeInt(str.length()); dout.writeChars(str); } } //}}} //{{{ writeStringArray() method private void writeStringArray(DataOutputStream dout, String[] str) throws IOException { if(str == null) { dout.writeInt(0); } else { dout.writeInt(str.length); for(int i = 0; i < str.length; i++) { writeString(dout,str[i]); } } } //}}} //{{{ writeBooleanArray() method private void writeBooleanArray(DataOutputStream dout, boolean[] bools) throws IOException { if(bools == null) { dout.writeInt(0); } else { dout.writeInt(bools.length); for(int i = 0; i < bools.length; i++) { dout.writeBoolean(bools[i]); } } } //}}} //{{{ writeMap() method private void writeMap(DataOutputStream dout, Map map) throws IOException { dout.writeInt(map.size()); Iterator iter = map.keySet().iterator(); while(iter.hasNext()) { String key = (String)iter.next(); writeString(dout,key); writeString(dout,map.get(key)); } } //}}} //}}} } //}}} }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.