File Source: PluginManager.java

         /* 
    P/P   *  Method: com.dmdirc.plugins.PluginManager__static_init
          */
     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  package com.dmdirc.plugins;
    23  
    24  import com.dmdirc.Main;
    25  import com.dmdirc.actions.ActionManager;
    26  import com.dmdirc.actions.CoreActionType;
    27  import com.dmdirc.actions.interfaces.ActionType;
    28  import com.dmdirc.config.IdentityManager;
    29  import com.dmdirc.config.prefs.PreferencesManager;
    30  import com.dmdirc.interfaces.ActionListener;
    31  import com.dmdirc.logger.Logger;
    32  import com.dmdirc.logger.ErrorLevel;
    33  import com.dmdirc.updater.components.PluginComponent;
    34  
    35  import java.io.File;
    36  import java.net.URL;
    37  import java.net.MalformedURLException;
    38  import java.util.ArrayList;
    39  import java.util.Collection;
    40  import java.util.Hashtable;
    41  import java.util.LinkedList;
    42  import java.util.List;
    43  import java.util.Map;
    44  import java.util.HashMap;
    45  
    46  /**
    47   * Searches for and manages plugins and services.
    48   *
    49   * @author shane
    50   * @author chris
    51   */
    52  public class PluginManager implements ActionListener {
    53  
    54      /** List of known plugins' file names to their corresponding {@link PluginInfo} objects. */
    55  	private final Map<String, PluginInfo> knownPlugins = new Hashtable<String, PluginInfo>();
    56  
    57  	/** Directory where plugins are stored. */
    58  	private final String myDir;
    59  
    60  	/** Singleton instance of the plugin manager. */
    61  	private static PluginManager me;
    62  
    63  	/** Map of services. */
    64  	private final Map<String, Map<String, Service>> services = new HashMap<String, Map<String, Service>>();
    65  	
    66  	/**
    67  	 * Creates a new instance of PluginManager.
    68  	 */
        	 /* 
    P/P 	  *  Method: void com.dmdirc.plugins.PluginManager()
        	  * 
        	  *  Presumptions:
        	  *    init'ed(com.dmdirc.actions.CoreActionType.CLIENT_PREFS_CLOSED)
        	  *    init'ed(com.dmdirc.actions.CoreActionType.CLIENT_PREFS_OPENED)
        	  * 
        	  *  Postconditions:
        	  *    java.lang.StringBuilder:toString(...)._tainted == 0
        	  *    this.knownPlugins == &amp;new Hashtable(PluginManager#1)
        	  *    this.myDir == &amp;java.lang.StringBuilder:toString(...)
        	  *    this.services == &amp;new HashMap(PluginManager#2)
        	  *    new HashMap(PluginManager#2) num objects == 1
        	  *    new Hashtable(PluginManager#1) num objects == 1
        	  */
    69  	private PluginManager() {
    70  		final String fs = System.getProperty("file.separator");
    71  		myDir = Main.getConfigDir() + "plugins" + fs;
    72  		ActionManager.addListener(this, CoreActionType.CLIENT_PREFS_OPENED, CoreActionType.CLIENT_PREFS_CLOSED);
    73  	}
    74  	
    75  	/**
    76  	 * Get a service object for the given name/type if one exists.
    77  	 *
    78  	 * @param type Type of this service
    79  	 * @param name Name of this service
    80  	 * @return The service requested, or null if service wasn't found and create wasn't specifed
    81  	 */
    82  	public Service getService(final String type, final String name) {
        		 /* 
    P/P 		  *  Method: Service getService(String, String)
        		  * 
        		  *  Preconditions:
        		  *    this.services != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  *    new ArrayList(Service#1) num objects == 0
        		  *    new Service(getService#1*) num objects == 0
        		  *    new Service(getService#3*) num objects == 0
        		  *    not_init'ed(new Service(getService#1*).name)
        		  *    not_init'ed(new Service(getService#3*).name)
        		  *    init'ed(new Service(getService#1*).serviceproviders)
        		  *    not_init'ed(new Service(getService#1*).type)
        		  *    not_init'ed(new Service(getService#3*).type)
        		  *    not_init'ed(new Service(getService#3*).serviceproviders)
        		  */
    83  		return getService(type, name, false);
    84  	}
    85  	
    86  	/**
    87  	 * Get a service object for the given name/type.
    88  	 *
    89  	 * @param type Type of this service
    90  	 * @param name Name of this service
    91  	 * @param create If the requested service doesn't exist, should it be created?
    92  	 * @return The service requested, or null if service wasn't found and create wasn't specifed
    93  	 */
    94  	public Service getService(final String type, final String name, final boolean create) {
    95  		// Find the type first
        		 /* 
    P/P 		  *  Method: Service getService(String, String, bool)
        		  * 
        		  *  Preconditions:
        		  *    this.services != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Map:get(...)@97 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  *    new ArrayList(Service#1) num objects <= 1
        		  *    new Service(getService#1) num objects <= 1
        		  *    new Service(getService#1).name == name
        		  *    init'ed(new Service(getService#1).name)
        		  *    init'ed(new Service(getService#1).serviceproviders)
        		  *    new Service(getService#1).type == type
        		  *    init'ed(new Service(getService#1).type)
        		  *    new Service(getService#3) num objects <= 1
        		  *    new Service(getService#3).name == name
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    create: {0}, {1}
        		  *    java.util.Map:containsKey(...)@96: {0}, {1}
        		  *    java.util.Map:containsKey(...)@99: {0}, {1}
        		  */
    96  		if (services.containsKey(type)) {
    97  			final Map<String, Service> map = services.get(type);
    98  			// Now the name
    99  			if (map.containsKey(name)) {
   100  				return map.get(name);
   101  			} else if (create) {
   102  				final Service service = new Service(type, name);
   103  				map.put(name, service);
   104  				return service;
   105  			}
   106  		} else if (create) {
   107  			final Map<String, Service> map = new HashMap<String, Service>();
   108  			final Service service = new Service(type, name);
   109  			map.put(name, service);
   110  			services.put(type, map);
   111  			return service;
   112  		}
   113  		
   114  		return null;
   115  	}
   116  	
   117  	/**
   118  	 * Get a ServiceProvider object for the given name/type if one exists.
   119  	 *
   120  	 * @param type Type of this service
   121  	 * @param name Name of this service
   122  	 * @return A ServiceProvider that provides the requested service.
   123  	 * @throws NoSuchProviderException If no provider exists for the requested service
   124  	 */
   125  	public ServiceProvider getServiceProvider(final String type, final String name) throws NoSuchProviderException {
        		 /* 
    P/P 		  *  Method: ServiceProvider getServiceProvider(String, String)
        		  * 
        		  *  Preconditions:
        		  *    this.services != null
        		  * 
        		  *  Postconditions:
        		  *    return_value != null
        		  */
   126  		final Service service = getService(type, name);
   127  		if (service != null) {
   128  			ServiceProvider provider = service.getActiveProvider();
   129  			if (provider != null) {
   130  				return provider;
   131  			} else {
   132  				// Try to activate the service then try again.
   133  				service.activate();
   134  				provider = service.getActiveProvider();
   135  				if (provider != null) {
   136  					return provider;
   137  				}
   138  			}
   139  		}
   140  		
   141  		throw new NoSuchProviderException("No provider found for: "+type+"->"+name);
   142  	}
   143  	
   144  	/**
   145  	 * Get a ServiceProvider object for the given tpye, prioritising those in the list of names.
   146  	 *
   147  	 * @param type Type to look for
   148  	 * @param names Names to look for
   149  	 * @param fallback Fallback to the first provider of type that exists if one from the list is not found.
   150  	 * @return A ServiceProvider that provides the requested service.
   151  	 * @throws NoSuchProviderException If no provider exists for the requested service and fallback is false, or no providers exist at all.
   152  	 */
   153  	public ServiceProvider getServiceProvider(final String type, final List<String> names, final boolean fallback) throws NoSuchProviderException {
        		 /* 
    P/P 		  *  Method: ServiceProvider getServiceProvider(String, List, bool)
        		  * 
        		  *  Preconditions:
        		  *    names != null
        		  *    (soft) fallback == 1
        		  *    (soft) this.services != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.List:get(...)@164 != null
        		  *    java.util.List:size(...)@163 >= 1
        		  * 
        		  *  Postconditions:
        		  *    return_value != null
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Iterator:hasNext(...)@154: {0}, {1}
        		  */
   154  		for (final String name : names) {
   155  			final ServiceProvider provider = getServiceProvider(type, name);
   156  			if (provider != null) {
   157  				return provider;
   158  			}
   159  		}
   160  		
   161  		if (fallback) {
   162  			final List<Service> servicesType = getServicesByType(type);
   163  			if (servicesType.size() > 0) {
   164  				final Service service = servicesType.get(0);
   165  				return getServiceProvider(type, service.getName());
   166  			}
   167  		}
   168  		
   169  		throw new NoSuchProviderException("No provider found for "+type+ "from the given list");
   170  	}
   171  	
   172  	/**
   173  	 * Get an ExportedService object of the given name from any provider that provides it.
   174  	 * This is the same as doing getServiceProvider("export", name).getExportedService(name)
   175  	 *
   176  	 * @param name Name of this service
   177  	 * @return An ExportedService object.
   178  	 * @throws NoSuchProviderException If no provider exists for the requested service.
   179  	 */
   180  	public ExportedService getExportedService(final String name) {
        		 /* 
    P/P 		  *  Method: ExportedService getExportedService(String)
        		  * 
        		  *  Preconditions:
        		  *    this.services != null
        		  *    (soft) com/dmdirc/plugins/GlobalClassLoader.me != null
        		  * 
        		  *  Presumptions:
        		  *    getServiceProvider(...).exports@181 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(com/dmdirc/plugins/GlobalClassLoader.me)
        		  *    return_value == One-of{&amp;new ExportedService(getExportedService#1*), &amp;new ExportedService(getExportedService#2*)}
        		  *    return_value in Addr_Set{&amp;new ExportedService(getExportedService#1*),&amp;new ExportedService(getExportedService#2*),&amp;new ExportedService(getExportedService#1*)}
        		  *    new ExportedService(getExportedService#1*) num objects <= 1
        		  *    init'ed(new ExportedService(getExportedService#1*).myMethod)
        		  *    init'ed(new ExportedService(getExportedService#1*).myObject)
        		  *    new ExportedService(getExportedService#1*).myObject == null
        		  *    new ExportedService(getExportedService#2*) num objects <= 1
        		  *    init'ed(new ExportedService(getExportedService#2*).myMethod)
        		  *    new ExportedService(getExportedService#2*).myObject == null
        		  *    ...
        		  */
   181  		return getServiceProvider("export", name).getExportedService(name);
   182  	}
   183  	
   184  	/**
   185  	 * Get a List of all services of a specifed type.
   186  	 *
   187  	 * @param type Type of service
   188  	 * @return The list of services requested.
   189  	 */
   190  	public List<Service> getServicesByType(final String type) {
   191  		// Find the type first
        		 /* 
    P/P 		  *  Method: List getServicesByType(String)
        		  * 
        		  *  Preconditions:
        		  *    this.services != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Map:get(...)@193 != null
        		  * 
        		  *  Postconditions:
        		  *    return_value in Addr_Set{&amp;new ArrayList(getServicesByType#2),&amp;new ArrayList(getServicesByType#1)}
        		  *    new ArrayList(getServicesByType#1) num objects <= 1
        		  *    new ArrayList(getServicesByType#2) num objects <= 1
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Map:containsKey(...)@192: {0}, {1}
        		  */
   192  		if (services.containsKey(type)) {
   193  			final Map<String, Service> map = services.get(type);
   194  			return new ArrayList<Service>(map.values());
   195  		}
   196  		
   197  		return new ArrayList<Service>();
   198  	}
   199  	
   200  	/**
   201  	 * Get a List of all services
   202  	 *
   203  	 * @return The list of all services.
   204  	 */
   205  	public List<Service> getAllServices() {
   206  		// Find the type first
        		 /* 
    P/P 		  *  Method: List getAllServices()
        		  * 
        		  *  Preconditions:
        		  *    this.services != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Iterator:next(...)@208 != null
        		  *    java.util.Map:values(...)@208 != null
        		  * 
        		  *  Postconditions:
        		  *    return_value == &amp;new ArrayList(getAllServices#1)
        		  *    new ArrayList(getAllServices#1) num objects == 1
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Iterator:hasNext(...)@208: {0}, {1}
        		  */
   207  		final List<Service> allServices = new ArrayList<Service>();
   208  		for (Map<String, Service> map : services.values()) {
   209  			allServices.addAll(map.values());
   210  		}
   211  		
   212  		return allServices;
   213  	}
   214  	
   215  	/**
   216  	 * Autoloads plugins.
   217  	 */
   218  	public void doAutoLoad() {
        		 /* 
    P/P 		  *  Method: void doAutoLoad()
        		  * 
        		  *  Preconditions:
        		  *    (soft) this.knownPlugins != null
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.ConfigManager:getOptionList(...)@219 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@219 != null
        		  *    java.util.Iterator:next(...)@219 != null
        		  *    java.util.Map:get(...)@348 != null
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:charAt(...)@221: {35}, {0..34, 36..216-1}
        		  *    java.lang.String:isEmpty(...)@221: {1}, {0}
        		  *    java.util.Iterator:hasNext(...)@219: {0}, {1}
        		  *    java.util.Map:get(...)@348: Addr_Set{null}, Inverse{null}
        		  */
   219  		for (String plugin : IdentityManager.getGlobalConfig().getOptionList("plugins", "autoload")) {
   220  			plugin = plugin.trim();
   221  			if (!plugin.isEmpty() && plugin.charAt(0) != '#' && getPluginInfo(plugin) != null) {
   222  				getPluginInfo(plugin).loadPlugin();
   223  			}
   224  		}
   225  	}
   226  
   227  	/**
   228  	 * Retrieves the singleton instance of the plugin manager.
   229  	 *
   230  	 * @return A singleton instance of PluginManager.
   231  	 */
   232  	public static final synchronized PluginManager getPluginManager() {
        		 /* 
    P/P 		  *  Method: PluginManager getPluginManager()
        		  * 
        		  *  Preconditions:
        		  *    init'ed(me)
        		  * 
        		  *  Postconditions:
        		  *    java.lang.StringBuilder:toString(...)._tainted == 0
        		  *    me == One-of{old me, &amp;new PluginManager(getPluginManager#1)}
        		  *    me != null
        		  *    return_value == me
        		  *    new HashMap(PluginManager#2) num objects <= 1
        		  *    new Hashtable(PluginManager#1) num objects <= 1
        		  *    new PluginClassLoader(getSubClassLoader#1) num objects == undefined
        		  *    new PluginClassLoader(getSubClassLoader#1) num objects == 0, if init'ed
        		  *    new PluginClassLoader(getSubClassLoader#1).pluginInfo == undefined
        		  *    new PluginClassLoader(getSubClassLoader#1).pluginInfo == null
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    me: Inverse{null}, Addr_Set{null}
        		  */
   233  		if (me == null) {
   234  			me = new PluginManager();
   235  			me.getPossiblePluginInfos(true);
   236  		}
   237  		
   238  		
   239  		return me;
   240  	}
   241  
   242  	/**
   243  	 * Tests and adds the specified plugin to the known plugins list. Plugins
   244  	 * will only be added if: <ul><li>The file exists,<li>No other plugin with
   245  	 * the same name is known,<li>All requirements are met for the plugin,
   246  	 * <li>The plugin has a valid config file that can be read</ul>.
   247  	 *
   248  	 * @param filename Filename of Plugin jar
   249  	 * @return True if the plugin is in the known plugins list (either before
   250  	 * this invocation or as a result of it), false if it was not added for
   251  	 * one of the reasons outlined above.
   252  	 */
   253  	public boolean addPlugin(final String filename) {
        		 /* 
    P/P 		  *  Method: bool addPlugin(String)
        		  * 
        		  *  Preconditions:
        		  *    filename != null
        		  *    this.knownPlugins != null
        		  *    (soft) com.dmdirc.logger.ErrorManager__static_init.new Class[](ErrorManager__static_init#2)[...] != null
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).logReports)
        		  *    (soft) com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).nextErrorID != null
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).sendReports)
        		  *    (soft) init'ed(com.dmdirc.ui.FatalErrorDialog$4__static_init.new int[](FatalErrorDialog$4__static_init#1)[...])
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread)
        		  *    (soft) init'ed(com/dmdirc/logger/ProgramError.errorDir)
        		  * 
        		  *  Presumptions:
        		  *    java.lang.String:isEmpty(...)@273 == 1
        		  *    pluginInfo.requirementsError@270 != null
        		  * 
        		  *  Postconditions:
        		  *    com/dmdirc/logger/ProgramError.errorDir == One-of{old com/dmdirc/logger/ProgramError.errorDir, &amp;new File(getErrorFile#1)}
        		  *    init'ed(com/dmdirc/logger/ProgramError.errorDir)
        		  *    init'ed(return_value)
        		  *    com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread == old com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread
        		  *    init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread)
        		  *    new ErrorReportingThread(sendError#1) num objects == 0
        		  *    init'ed(new ErrorReportingThread(sendError#1).queue)
        		  *    new File(getErrorFile#1) num objects <= 1
        		  * 
        		  *  Test Vectors:
        		  *    java.io.File:exists(...)@258: {1}, {0}
        		  *    java.util.Map:containsKey(...)@254: {0}, {1}
        		  */
   254  		if (knownPlugins.containsKey(filename.toLowerCase())) {
   255  			return true;
   256  		}
   257  		
   258  		if (!new File(getDirectory() + filename).exists()) {
   259  			Logger.userError(ErrorLevel.MEDIUM, "Error loading plugin " + filename + ": File does not exist");
   260  			return false;
   261  		}
   262  		
   263  		try {
   264  			final PluginInfo pluginInfo = new PluginInfo(new URL("file:"+getDirectory()+filename));
   265  			final PluginInfo existing = getPluginInfoByName(pluginInfo.getName());
   266  			if (existing != null) {
   267  				Logger.userError(ErrorLevel.MEDIUM, "Duplicate Plugin detected, Ignoring. (" + filename + " is the same as " + existing.getFilename() + ")");
   268  				return false;
   269  			}
   270  			new PluginComponent(pluginInfo);
   271  			
   272  			final String requirements = pluginInfo.getRequirementsError();
   273  			if (requirements.isEmpty()) {
   274  				knownPlugins.put(filename.toLowerCase(), pluginInfo);
   275  			
   276  				return true;
   277  			} else {
   278  				throw new PluginException("Plugin "+filename+" was not loaded, one or more requirements not met ("+requirements+")");
   279  			}
   280  		} catch (MalformedURLException mue) {
   281  			Logger.userError(ErrorLevel.MEDIUM, "Error creating URL for plugin " + filename + ": " + mue.getMessage(), mue);
   282  		} catch (PluginException e) {
   283  			Logger.userError(ErrorLevel.MEDIUM, "Error loading plugin " + filename + ": " + e.getMessage(), e);
   284  		}
   285  		
   286  		return false;
   287  	}
   288  
   289  	/**
   290  	 * Remove a plugin.
   291  	 *
   292  	 * @param filename Filename of Plugin jar
   293  	 * @return True if removed.
   294  	 */
   295  	public boolean delPlugin(final String filename) {
        		 /* 
    P/P 		  *  Method: bool delPlugin(String)
        		  * 
        		  *  Preconditions:
        		  *    filename != null
        		  *    this.knownPlugins != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Map:get(...)@348 != null
        		  *    pluginInfo.children@300 != null
        		  *    pluginInfo.plugin@300 != null
        		  *    pluginInfo.provides@300 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
        		  *    possibly_updated(java.lang.StringBuilder:toString(...)._tainted)
        		  *    init'ed(return_value)
        		  *    init'ed(new PluginClassLoader(getSubClassLoader#1) num objects)
        		  *    possibly_updated(new PluginClassLoader(getSubClassLoader#1).pluginInfo)
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Map:containsKey(...)@296: {1}, {0}
        		  */
   296  		if (!knownPlugins.containsKey(filename.toLowerCase())) {
   297  			return false;
   298  		}
   299  
   300  		PluginInfo pluginInfo = getPluginInfo(filename);
   301  		
   302  		pluginInfo.unloadPlugin();
   303  		
   304  		knownPlugins.remove(filename.toLowerCase());
   305  
   306  		return true;
   307  	}
   308  
   309  	/**
   310  	 * Reload a plugin.
   311  	 *
   312  	 * @param filename Filename of Plugin jar
   313  	 * @return True if reloaded.
   314  	 */
   315  	public boolean reloadPlugin(final String filename) {
        		 /* 
    P/P 		  *  Method: bool reloadPlugin(String)
        		  * 
        		  *  Preconditions:
        		  *    filename != null
        		  *    this.knownPlugins != null
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread)
        		  *    (soft) init'ed(com/dmdirc/logger/ProgramError.errorDir)
        		  * 
        		  *  Presumptions:
        		  *    java.util.Map:get(...)@348 != null
        		  * 
        		  *  Postconditions:
        		  *    com/dmdirc/logger/ProgramError.errorDir == old com/dmdirc/logger/ProgramError.errorDir
        		  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
        		  *    possibly_updated(java.lang.StringBuilder:toString(...)._tainted)
        		  *    init'ed(return_value)
        		  *    com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread == old com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread
        		  *    new ErrorReportingThread(sendError#1) num objects == undefined
        		  *    new ErrorReportingThread(sendError#1) num objects == 0, if init'ed
        		  *    new File(getErrorFile#1) num objects == new ErrorReportingThread(sendError#1) num objects
        		  *    new ErrorReportingThread(sendError#1).queue == undefined
        		  *    new ErrorReportingThread(sendError#1).queue == null
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    addPlugin(...)@322: {0}, {1}
        		  *    java.util.Map:containsKey(...)@316: {1}, {0}
        		  */
   316  		if (!knownPlugins.containsKey(filename.toLowerCase())) {
   317  			return false;
   318  		}
   319  		
   320  		final boolean wasLoaded = getPluginInfo(filename).isLoaded();
   321  		delPlugin(filename);
   322  		boolean result = addPlugin(filename);
   323  		
   324  		if (wasLoaded && result) {
   325  			getPluginInfo(filename).loadPlugin();
   326  			result = getPluginInfo(filename).isLoaded();
   327  		}
   328  		
   329  		return result;
   330  	}
   331  
   332  	/**
   333  	 * Reload all plugins.
   334  	 */
   335  	public void reloadAllPlugins() {
        		 /* 
    P/P 		  *  Method: void reloadAllPlugins()
        		  * 
        		  *  Preconditions:
        		  *    this.knownPlugins != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Iterator:next(...)@336 != null
        		  * 
        		  *  Postconditions:
        		  *    java.lang.StringBuilder:toString(...)._tainted == 0
        		  *    new PluginClassLoader(getSubClassLoader#1) num objects == undefined
        		  *    new PluginClassLoader(getSubClassLoader#1) num objects == 0, if init'ed
        		  *    new PluginClassLoader(getSubClassLoader#1).pluginInfo == undefined
        		  *    new PluginClassLoader(getSubClassLoader#1).pluginInfo == null
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Iterator:hasNext(...)@336: {0}, {1}
        		  */
   336  		for (PluginInfo pluginInfo : getPluginInfos()) {
   337  			reloadPlugin(pluginInfo.getFilename());
   338  		}
   339  	}
   340  
   341  	/**
   342  	 * Get a plugin instance.
   343  	 *
   344  	 * @param filename File name of plugin jar
   345  	 * @return PluginInfo instance, or null
   346  	 */
   347  	public PluginInfo getPluginInfo(final String filename) {
        		 /* 
    P/P 		  *  Method: PluginInfo getPluginInfo(String)
        		  * 
        		  *  Preconditions:
        		  *    filename != null
        		  *    this.knownPlugins != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  */
   348  		return knownPlugins.get(filename.toLowerCase());
   349  	}
   350  
   351  	/**
   352  	 * Get a plugin instance by plugin name.
   353  	 *
   354  	 * @param name Name of plugin to find.
   355  	 * @return PluginInfo instance, or null
   356  	 */
   357  	public PluginInfo getPluginInfoByName(final String name) {
        		 /* 
    P/P 		  *  Method: PluginInfo getPluginInfoByName(String)
        		  * 
        		  *  Preconditions:
        		  *    this.knownPlugins != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Iterator:next(...)@358 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:equalsIgnoreCase(...)@359: {0}, {1}
        		  *    java.util.Iterator:hasNext(...)@358: {0}, {1}
        		  */
   358  		for (PluginInfo pluginInfo : getPluginInfos()) {
   359  			if (pluginInfo.getName().equalsIgnoreCase(name)) {
   360  				return pluginInfo;
   361  			}
   362  		}
   363  		
   364  		return null;
   365  	}
   366  
   367  	/**
   368  	 * Get directory where plugins are stored.
   369  	 *
   370  	 * @return Directory where plugins are stored.
   371  	 */
   372  	public String getDirectory() {
        		 /* 
    P/P 		  *  Method: String getDirectory()
        		  * 
        		  *  Postconditions:
        		  *    return_value == this.myDir
        		  *    init'ed(return_value)
        		  */
   373  		return myDir;
   374  	}
   375  
   376  	/**
   377  	 * Retrieves a list of all installed plugins.
   378  	 * Any file under the main plugin directory (~/.DMDirc/plugins or similar)
   379  	 * that matches *.jar is deemed to be a valid plugin.
   380  	 *
   381  	 * @param addPlugins Should all found plugins be automatically have addPlugin() called?
   382  	 * @return A list of all installed plugins
   383  	 */
   384  	public List<PluginInfo> getPossiblePluginInfos(final boolean addPlugins) {
        		 /* 
    P/P 		  *  Method: List getPossiblePluginInfos(bool)
        		  * 
        		  *  Preconditions:
        		  *    (soft) com.dmdirc.logger.ErrorManager__static_init.new Class[](ErrorManager__static_init#2)[...] != null
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).logReports)
        		  *    (soft) com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).nextErrorID != null
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).sendReports)
        		  *    (soft) init'ed(com.dmdirc.ui.FatalErrorDialog$4__static_init.new int[](FatalErrorDialog$4__static_init#1)[...])
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread)
        		  *    (soft) init'ed(com/dmdirc/logger/ProgramError.errorDir)
        		  *    (soft) this.knownPlugins != null
        		  *    (soft) this.myDir != null
        		  * 
        		  *  Presumptions:
        		  *    arr$.length@394 <= 232-1
        		  *    java.io.File:getName(...)@397 != null
        		  *    java.io.File:getPath(...)@398 != null
        		  *    java.io.File:listFiles(...)@394 != null
        		  *    java.util.Iterator:next(...)@416 != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(com/dmdirc/logger/ProgramError.errorDir)
        		  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
        		  *    possibly_updated(java.lang.StringBuilder:toString(...)._tainted)
        		  *    return_value == &amp;new LinkedList(getPossiblePluginInfos#10)
        		  *    init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread)
        		  *    init'ed(new ErrorReportingThread(sendError#1) num objects)
        		  *    new ErrorReportingThread(sendError#1) num objects == undefined
        		  *    new ErrorReportingThread(sendError#1) num objects == 0, if init'ed
        		  *    new File(getErrorFile#1) num objects == new ErrorReportingThread(sendError#1) num objects
        		  *    init'ed(new ErrorReportingThread(sendError#1).queue)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    addPlugins: {0}, {1}
        		  *    java.io.File:exists(...)@417: {1}, {0}
        		  *    java.io.File:isDirectory(...)@393: {0}, {1}
        		  *    java.io.File:isFile(...)@397: {0}, {1}
        		  *    java.lang.String:endsWith(...)@397: {0}, {1}
        		  *    java.util.Iterator:hasNext(...)@416: {0}, {1}
        		  *    java.util.LinkedList:isEmpty(...)@391: {1}, {0}
        		  */
   385  		final Map<String, PluginInfo> res = new Hashtable<String, PluginInfo>();
   386  		
   387  		final LinkedList<File> dirs = new LinkedList<File>();
   388  		
   389  		dirs.add(new File(myDir));
   390  		
   391  		while (!dirs.isEmpty()) {
   392  			final File dir = dirs.pop();
   393  			if (dir.isDirectory()) {
   394  				for (File file : dir.listFiles()) {
   395  					dirs.add(file);
   396  				}
   397  			} else if (dir.isFile() && dir.getName().endsWith(".jar")) {
   398  				String target = dir.getPath();
   399  				
   400  				// Remove the plugin dir
   401  				target = target.substring(myDir.length(), target.length());
   402  				if (addPlugins) {
   403  					addPlugin(target);
   404  				} else {
   405  					try {
   406  						final PluginInfo pi = new PluginInfo(new URL("file:"+getDirectory()+target), false);
   407  						res.put(target, pi);
   408  					} catch (MalformedURLException mue) {
   409  						Logger.userError(ErrorLevel.MEDIUM, "Error creating URL for plugin " + target + ": " + mue.getMessage(), mue);
   410  					} catch (PluginException pe) { /* This can not be thrown when the second param is false */}
   411  				}
   412  			}
   413  		}
   414  
   415  		final Map<String, PluginInfo> knownPluginsCopy = new Hashtable<String, PluginInfo>(knownPlugins);
   416  		for (PluginInfo pi : knownPluginsCopy.values()) {
   417  			if (!(new File(pi.getFullFilename())).exists()) {
   418  				delPlugin(pi.getFilename());
   419  			} else if (addPlugins) {
   420  				res.put(pi.getFilename().toLowerCase(), pi);
   421  			}
   422  		}
   423  		
   424  		return new LinkedList<PluginInfo>(res.values());
   425  	}
   426  
   427  	/**
   428  	 * Update the autoLoadList
   429  	 *
   430  	 * @param plugin to add/remove (Decided automatically based on isLoaded())
   431  	 */
   432  	public void updateAutoLoad(final PluginInfo plugin) {
        		 /* 
    P/P 		  *  Method: void updateAutoLoad(PluginInfo)
        		  * 
        		  *  Preconditions:
        		  *    plugin != null
        		  *    init'ed(plugin.plugin)
        		  *    (soft) init'ed(plugin.tempLoaded)
        		  * 
        		  *  Presumptions:
        		  *    com.dmdirc.config.ConfigManager:getOptionList(...)@433 != null
        		  *    com.dmdirc.config.IdentityManager:getConfigIdentity(...)@442 != null
        		  *    com.dmdirc.config.IdentityManager:getGlobalConfig(...)@433 != null
        		  * 
        		  *  Test Vectors:
        		  *    !(plugin.plugin == null) &amp; plugin.tempLoaded == 0: {0}, {1}
        		  *    java.util.List:contains(...)@436: {1}, {0}
        		  *    java.util.List:contains(...)@438: {0}, {1}
        		  */
   433  		final List<String> list = IdentityManager.getGlobalConfig().getOptionList("plugins", "autoload");
   434          final String path = plugin.getRelativeFilename();
   435  		
   436  		if (plugin.isLoaded() && !list.contains(path)) {
   437  			list.add(path);
   438  		} else if (!plugin.isLoaded() && list.contains(path)) {
   439  			list.remove(path);
   440  		}
   441  		
   442  		IdentityManager.getConfigIdentity().setOption("plugins", "autoload", list);
   443  	}
   444  	
   445  	/**
   446  	 * Get Collection<PluginInfo> of known plugins.
   447  	 *
   448  	 * @return Collection<PluginInfo> of known plugins.
   449  	 */
   450  	public Collection<PluginInfo> getPluginInfos() {
        		 /* 
    P/P 		  *  Method: Collection getPluginInfos()
        		  * 
        		  *  Preconditions:
        		  *    this.knownPlugins != null
        		  * 
        		  *  Postconditions:
        		  *    return_value == &amp;new ArrayList(getPluginInfos#1)
        		  *    new ArrayList(getPluginInfos#1) num objects == 1
        		  */
   451  		return new ArrayList<PluginInfo>(knownPlugins.values());
   452  	}
   453  
   454  	/** {@inheritDoc} */
   455  	@Override
   456  	public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
        		 /* 
    P/P 		  *  Method: void processEvent(ActionType, StringBuffer, Object[])
        		  * 
        		  *  Preconditions:
        		  *    type != null
        		  *    (soft) arguments != null
        		  *    (soft) arguments.length >= 1
        		  *    (soft) init'ed(arguments[0])
        		  *    (soft) com.dmdirc.logger.ErrorManager__static_init.new Class[](ErrorManager__static_init#2)[...] != null
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).logReports)
        		  *    (soft) com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).nextErrorID != null
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).sendReports)
        		  *    (soft) init'ed(com.dmdirc.ui.FatalErrorDialog$4__static_init.new int[](FatalErrorDialog$4__static_init#1)[...])
        		  *    (soft) init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread)
        		  *    ...
        		  * 
        		  *  Presumptions:
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CLIENT_PREFS_CLOSED)
        		  *    init'ed(com.dmdirc.actions.CoreActionType.CLIENT_PREFS_OPENED)
        		  *    java.util.Iterator:next(...)@458 != null
        		  *    java.util.Iterator:next(...)@471 != null
        		  *    pi.children@471 != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(com/dmdirc/logger/ProgramError.errorDir)
        		  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
        		  *    possibly_updated(java.lang.StringBuilder:toString(...)._tainted)
        		  *    init'ed(com.dmdirc.logger.ErrorManager__static_init.new ErrorManager(ErrorManager__static_init#1).reportThread)
        		  *    init'ed(new ErrorReportingThread(sendError#1) num objects)
        		  *    init'ed(new ErrorReportingThread(sendError#1).queue)
        		  *    init'ed(new File(getErrorFile#1) num objects)
        		  *    init'ed(new PluginClassLoader(getSubClassLoader#1) num objects)
        		  *    init'ed(new PluginClassLoader(getSubClassLoader#1).pluginInfo)
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.Object:equals(...)@457: {0}, {1}
        		  *    java.lang.Object:equals(...)@470: {0}, {1}
        		  *    java.util.Iterator:hasNext(...)@471: {0}, {1}
        		  *    pi.tempLoaded@458: {0}, {1}
        		  */
   457  		if (type.equals(CoreActionType.CLIENT_PREFS_OPENED)) {
   458  			for (PluginInfo pi : getPluginInfos()) {
   459  				if (!pi.isLoaded() && !pi.isTempLoaded()) {
   460  					pi.loadPluginTemp();
   461  				}
   462  				if (pi.isLoaded() || pi.isTempLoaded()) {
   463  					try {
   464  						pi.getPlugin().showConfig((PreferencesManager) arguments[0]);
   465  					} catch (Throwable t) {
   466  						Logger.userError(ErrorLevel.MEDIUM, "Error with plugin ("+pi.getNiceName()+"), unable to show config ("+t+")", t);
   467  					}
   468  				}
   469  			}
   470  		} else if (type.equals(CoreActionType.CLIENT_PREFS_CLOSED)) {
   471  			for (PluginInfo pi : getPluginInfos()) {
   472  				if (pi.isTempLoaded()) {
   473  					pi.unloadPlugin();
   474  				}
   475  			}
   476  		}
   477  	}
   478  }








SofCheck Inspector Build Version : 2.17854
PluginManager.java 2009-Jun-25 01:54:24
PluginManager.class 2009-Sep-02 17:04:12