File Source: PluginInfo.java
/*
P/P * Method: com.dmdirc.plugins.PluginInfo$1__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.actions.ActionManager;
25 import com.dmdirc.actions.CoreActionType;
26 import com.dmdirc.config.Identity;
27 import com.dmdirc.config.IdentityManager;
28 import com.dmdirc.config.prefs.validator.ValidationResponse;
29 import com.dmdirc.util.resourcemanager.ResourceManager;
30 import com.dmdirc.util.ConfigFile;
31 import com.dmdirc.util.InvalidConfigFileException;
32 import com.dmdirc.logger.Logger;
33 import com.dmdirc.logger.ErrorLevel;
34
35 import com.dmdirc.updater.Version;
36 import java.io.File;
37 import java.io.IOException;
38 import java.lang.reflect.Constructor;
39 import java.lang.reflect.InvocationTargetException;
40 import java.util.List;
41 import java.util.Properties;
42 import java.util.Map;
43 import java.util.HashMap;
44 import java.util.ArrayList;
45 import java.util.Timer;
46 import java.util.TimerTask;
47 import java.net.URL;
48
/*
P/P * Method: ResourceManager access$002(PluginInfo, ResourceManager)
*
* Preconditions:
* x0 != null
*
* Postconditions:
* return_value == x1
* init'ed(return_value)
* x0.myResourceManager == return_value
*/
49 public class PluginInfo implements Comparable<PluginInfo>, ServiceProvider {
50
51 /** A logger for this class. */
/*
P/P * Method: com.dmdirc.plugins.PluginInfo__static_init
*
* Postconditions:
* init'ed(LOGGER)
*/
52 private static final java.util.logging.Logger LOGGER = java.util.logging
53 .Logger.getLogger(PluginInfo.class.getName());
54
55 /** Plugin Meta Data */
56 private ConfigFile metaData = null;
57 /** URL that this plugin was loaded from */
58 private final URL url;
59 /** Filename for this plugin (taken from URL) */
60 private final String filename;
61 /** The actual Plugin from this jar */
62 private Plugin plugin = null;
63 /** The classloader used for this Plugin */
64 private PluginClassLoader classloader = null;
65 /** The resource manager used by this pluginInfo */
66 private ResourceManager myResourceManager = null;
67 /** Is this plugin only loaded temporarily? */
68 private boolean tempLoaded = false;
69 /** List of classes this plugin has */
70 private List<String> myClasses = new ArrayList<String>();
71 /** Requirements error message. */
72 private String requirementsError = "";
73
74 /** Last Error Message. */
75 private String lastError = "No Error";
76
77 /** Are we trying to load? */
78 private boolean isLoading = false;
79
80 /** Is this plugin using a migrated config? */
81 private boolean migrated = false;
82
83 /** List of services we provide. */
84 private final List<Service> provides = new ArrayList<Service>();
85
86 /** List of children of this plugin. */
87 private final List<PluginInfo> children = new ArrayList<PluginInfo>();
88
89 /** Map of exports */
90 private final Map<String, ExportInfo> exports = new HashMap<String, ExportInfo>();
91
92 /**
93 * Create a new PluginInfo.
94 *
95 * @param url URL to file that this plugin is stored in.
96 * @throws PluginException if there is an error loading the Plugin
97 * @since 0.6
98 */
99 public PluginInfo(final URL url) throws PluginException {
/*
P/P * Method: void com.dmdirc.plugins.PluginInfo(URL)
*/
100 this(url, true);
101 }
102
103 /**
104 * Create a new PluginInfo.
105 *
106 * @param url URL to file that this plugin is stored in.
107 * @param load Should this plugin be loaded, or is this just a placeholder? (true for load, false for placeholder)
108 * @throws PluginException if there is an error loading the Plugin
109 * @since 0.6
110 */
/*
P/P * Method: void com.dmdirc.plugins.PluginInfo(URL, bool)
*
* Preconditions:
* url != null
*
* Presumptions:
* com.dmdirc.updater.Version:isValid(...)@153 == 1
* com.dmdirc.util.resourcemanager.ResourceManager:getResourcesStartingWith(...)@174 != null
* com.dmdirc.util.resourcemanager.ResourceManager:resourceExists(...)@169 == 1
* java.lang.String:isEmpty(...)@156 == 0
* java.lang.String:isEmpty(...)@159 == 0
* ...
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == 0
* this.children == &new ArrayList(PluginInfo#3)
* init'ed(this.classloader)
* this.exports == &new HashMap(PluginInfo#4)
* init'ed(this.filename)
* init'ed(this.isLoading)
* this.lastError == &"No Error"
* this.metaData in Addr_Set{null,&new ConfigFile(getConfigFile#1),&new ConfigFile(getMigratedConfigFile#1),&new ConfigFile(getConfigFile#1),&new ConfigFile(getMigratedConfigFile#1)}
* init'ed(this.migrated)
* this.myClasses == &new ArrayList(PluginInfo#1)
* ...
*
* Test Vectors:
* load: {1}, {0}
* java.io.File:delete(...)@118: {0}, {1}
* java.io.File:exists(...)@118: {0}, {1}
* java.lang.String:matches(...)@176: {0}, {1}
* java.util.Iterator:hasNext(...)@174: {0}, {1}
*/
111 public PluginInfo(final URL url, final boolean load) throws PluginException {
112 this.url = url;
113 this.filename = (new File(url.getPath())).getName();
114
115 ResourceManager res;
116
117 // Check for updates.
118 if (new File(getFullFilename()+".update").exists() && new File(getFullFilename()).delete()) {
119 new File(getFullFilename()+".update").renameTo(new File(getFullFilename()));
120 }
121
122 if (!load) {
123 // Load the metaData if available.
124 try {
125 metaData = getConfigFile();
126 } catch (IOException ioe) {
127 metaData = null;
128 }
129 return;
130 }
131
132 try {
133 res = getResourceManager();
134 } catch (IOException ioe) {
135 lastError = "Error with resourcemanager: "+ioe.getMessage();
136 throw new PluginException("Plugin "+filename+" failed to load. "+lastError, ioe);
137 }
138
139 try {
140 metaData = getConfigFile();
141 if (metaData == null) {
142 lastError = "plugin.config doesn't exist in jar";
143 throw new PluginException("Plugin "+filename+" failed to load. "+lastError);
144 }
145 } catch (IOException e) {
146 lastError = "plugin.config IOException: "+e.getMessage();
147 throw new PluginException("Plugin "+filename+" failed to load, plugin.config failed to open - "+e.getMessage(), e);
148 } catch (IllegalArgumentException e) {
149 lastError = "plugin.config IllegalArgumentException: "+e.getMessage();
150 throw new PluginException("Plugin "+filename+" failed to load, plugin.config failed to open - "+e.getMessage(), e);
151 }
152
153 if (!getVersion().isValid()) {
154 lastError = "Incomplete plugin.config (Missing or invalid 'version')";
155 throw new PluginException("Plugin "+filename+" failed to load. "+lastError);
156 } else if (getAuthor().isEmpty()) {
157 lastError = "Incomplete plugin.config (Missing or invalid 'author')";
158 throw new PluginException("Plugin "+filename+" failed to load. "+lastError);
159 } else if (getName().isEmpty()) {
160 lastError = "Incomplete plugin.config (Missing or invalid 'name')";
161 throw new PluginException("Plugin "+filename+" failed to load. "+lastError);
162 } else if (getMainClass().isEmpty()) {
163 lastError = "Incomplete plugin.config (Missing or invalid 'mainclass')";
164 throw new PluginException("Plugin "+filename+" failed to load. "+lastError);
165 }
166
167 if (checkRequirements(true)) {
168 final String mainClass = getMainClass().replace('.', '/')+".class";
169 if (!res.resourceExists(mainClass)) {
170 lastError = "main class file ("+mainClass+") not found in jar.";
171 throw new PluginException("Plugin "+filename+" failed to load. "+lastError);
172 }
173
174 for (final String classfilename : res.getResourcesStartingWith("")) {
175 String classname = classfilename.replace('/', '.');
176 if (classname.matches("^.*\\.class$")) {
177 classname = classname.replaceAll("\\.class$", "");
178 myClasses.add(classname);
179 }
180 }
181
182 if (isPersistent() && loadAll()) { loadEntirePlugin(); }
183 } else {
184 lastError = "One or more requirements not met ("+requirementsError+")";
185 throw new PluginException("Plugin "+filename+" was not loaded. "+lastError);
186 }
187
188 updateProvides();
189 getDefaults();
190 }
191
192 /**
193 * Get misc meta-information.
194 *
195 * @param properties The properties file to look in
196 * @param metainfo The metainfos to look for in order. If the first item in
197 * the array is not found, the next will be looked for, and
198 * so on until either one is found, or none are found.
199 * @param fallback Fallback value if requested values are not found
200 * @return Misc Meta Info (or "" if none are found);
201 */
202 private String getMetaInfo(final Properties properties, final String[] metainfo, final String fallback) {
/*
P/P * Method: String getMetaInfo(Properties, String[], String)
*
* Preconditions:
* metainfo != null
* metainfo.length <= 232-1
* (soft) init'ed(metainfo[...])
* (soft) properties != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* java.util.Properties:getProperty(...)@204: Addr_Set{null}, Inverse{null}
*/
203 for (String meta : metainfo) {
204 String result = properties.getProperty(meta);
205 if (result != null) { return result; }
206 }
207 return fallback;
208 }
209
210 /**
211 * Return a ConfigFile that has been migrated from a Properties file.
212 *
213 * @return ConfigFile object with data from plugin.info properties file
214 */
215 private ConfigFile getMigratedConfigFile() throws IOException {
/*
P/P * Method: ConfigFile getMigratedConfigFile()
*
* Preconditions:
* init'ed(this.myResourceManager)
* (soft) this.url != null
*
* Presumptions:
* java.util.Iterator:next(...)@266 != null
* java.util.Map_Entry:getKey(...)@267 != null
* java.util.Map_Entry:getValue(...)@268 != null
* java.util.Properties:entrySet(...)@266 != null
*
* Postconditions:
* return_value == &new ConfigFile(getMigratedConfigFile#1)
* this.migrated == 1
* new ConfigFile(getMigratedConfigFile#1) num objects == 1
* this.myResourceManager != null
*
* Test Vectors:
* java.lang.String:startsWith(...)@275: {0}, {1}
* java.util.Iterator:hasNext(...)@266: {0}, {1}
* java.util.Properties:containsKey(...)@238: {0}, {1}
* java.util.Properties:containsKey(...)@241: {0}, {1}
* java.util.Properties:containsKey(...)@252: {0}, {1}
* java.util.Properties:containsKey(...)@257: {0}, {1}
* java.util.Properties:containsKey(...)@261: {0}, {1}
*/
216 final ResourceManager res = getResourceManager();
217 final ConfigFile file = new ConfigFile(res.getResourceInputStream("META-INF/plugin.config"));
218 migrated = true;
219
220 // Logger.userError(ErrorLevel.LOW, "Plugin '"+getFilename()+"' is using an older plugin.info file, check for updates.");
221 final Properties old = new Properties();
222 old.load(res.getResourceInputStream("META-INF/plugin.info"));
223
224 final Map<String, String> meta = new HashMap<String, String>();
225 final Map<String, String> requires = new HashMap<String, String>();
226 final Map<String, String> updates = new HashMap<String, String>();
227 final Map<String, String> version = new HashMap<String, String>();
228 final Map<String, String> misc = new HashMap<String, String>();
229 final List<String> persistent = new ArrayList<String>();
230 final List<String> provides = new ArrayList<String>();
231 final List<String> required_services = new ArrayList<String>();
232
233 meta.put("name", old.getProperty("name", ""));
234 meta.put("author", old.getProperty("author", ""));
235 meta.put("description", old.getProperty("description", ""));
236 meta.put("mainclass", old.getProperty("mainclass", ""));
237
238 if (old.containsKey("nicename")) {
239 meta.put("nicename", old.getProperty("nicename", ""));
240 }
241 if (old.containsKey("loadall")) {
242 meta.put("loadall", old.getProperty("loadall", "no"));
243 }
244
245 requires.put("os", getMetaInfo(old, new String[]{"required-os", "require-os"}, ""));
246 requires.put("files", getMetaInfo(old, new String[]{"required-files", "require-files", "required-files", "require-files"}, ""));
247 requires.put("plugins", getMetaInfo(old, new String[]{"required-plugins", "require-plugins", "required-plugin", "require-plugin"}, ""));
248 requires.put("ui", getMetaInfo(old, new String[]{"required-ui", "require-ui"}, ""));
249
250 requires.put("dmdirc", old.getProperty("minversion", "0") + "-" + old.getProperty("maxversion", ""));
251
252 if (old.containsKey("addonid")) {
253 updates.put("id", old.getProperty("addonid", ""));
254 }
255
256 version.put("number", old.getProperty("version", "0"));
257 if (old.containsKey("friendlyversion")) {
258 version.put("friendly", old.getProperty("friendlyversion", ""));
259 }
260
261 final boolean hasPersistent = old.containsKey("persistent");
262 if (hasPersistent) {
263 persistent.add("*");
264 }
265
266 for (Map.Entry entry : old.entrySet()) {
267 final String key = entry.getKey().toString();
268 final String value = entry.getValue().toString();
269
270 // For compatability reasons, add the contents of the file to the "misc"
271 // key section, to allow getMetaInfo() compatability for old files.
272 misc.put(key, value);
273
274 // Also handle persistent items here
275 if (!hasPersistent && key.toLowerCase().startsWith("persistent-")) {
276 persistent.add(key.substring(11));
277 }
278 }
279
280 file.addDomain("metadata", meta);
281 file.addDomain("requires", requires);
282 file.addDomain("updates", updates);
283 file.addDomain("version", version);
284 file.addDomain("misc", misc);
285 file.addDomain("persistent", persistent);
286 file.addDomain("provides", provides);
287 file.addDomain("required-services", required_services);
288
289 return file;
290 }
291
292 /**
293 * Get a ConfigFile object for this plugin.
294 * This will load a ConfigFile
295 *
296 * @return the ConfigFile object for this plugin, or null if the plugin has no config
297 */
298 private ConfigFile getConfigFile() throws IOException {
/*
P/P * Method: ConfigFile getConfigFile()
*
* Preconditions:
* init'ed(this.myResourceManager)
* (soft) this.url != null
*
* Presumptions:
* com.dmdirc.util.resourcemanager.ResourceManager:resourceExists(...)@306 == 1
*
* Postconditions:
* return_value in Addr_Set{null,&new ConfigFile(getMigratedConfigFile#1),&new ConfigFile(getConfigFile#1),&new ConfigFile(getMigratedConfigFile#1)}
* possibly_updated(this.migrated)
* this.myResourceManager != null
* new ConfigFile(getConfigFile#1) num objects <= 1
* new ConfigFile(getMigratedConfigFile#1) num objects <= 1
*
* Test Vectors:
* com.dmdirc.util.resourcemanager.ResourceManager:resourceExists(...)@301: {0}, {1}
* com.dmdirc.util.resourcemanager.ResourceManager:resourceExists(...)@312: {0}, {1}
*/
299 ConfigFile file = null;
300 final ResourceManager res = getResourceManager();
301 if (res.resourceExists("META-INF/plugin.config")) {
302 try {
303 file = new ConfigFile(res.getResourceInputStream("META-INF/plugin.config"));
304 file.read();
305 } catch (InvalidConfigFileException icfe) {
306 if (res.resourceExists("META-INF/plugin.info")) {
307 file = getMigratedConfigFile();
308 } else {
309 throw new IOException("Unable to read plugin.config", icfe);
310 }
311 }
312 } else if (res.resourceExists("META-INF/plugin.info")) {
313 file = getMigratedConfigFile();
314 }
315
316 return file;
317 }
318
319 /**
320 * Get the defaults, formatters and icons for this plugin.
321 */
322 private void getDefaults() {
/*
P/P * Method: void getDefaults()
*
* Preconditions:
* init'ed(this.metaData)
*
* Presumptions:
* com.dmdirc.config.IdentityManager:getAddonIdentity(...)@325 != null
* com.dmdirc.util.ConfigFile:getKeyDomain(...)@331 != null
* com.dmdirc.util.ConfigFile:getKeyDomain(...)@342 != null
* com.dmdirc.util.ConfigFile:getKeyDomain(...)@353 != null
* java.util.Iterator:next(...)@333 != null
* ...
*
* Test Vectors:
* this.metaData: Inverse{null}, Addr_Set{null}
* com.dmdirc.util.ConfigFile:isKeyDomain(...)@330: {0}, {1}
* com.dmdirc.util.ConfigFile:isKeyDomain(...)@341: {0}, {1}
* com.dmdirc.util.ConfigFile:isKeyDomain(...)@352: {0}, {1}
* java.util.Iterator:hasNext(...)@333: {0}, {1}
* java.util.Iterator:hasNext(...)@344: {0}, {1}
* java.util.Iterator:hasNext(...)@355: {0}, {1}
*/
323 if (metaData == null) { return; }
324
325 final Identity defaults = IdentityManager.getAddonIdentity();
326 final String domain = "plugin-"+getName();
327
328 LOGGER.finer(getName() + ": Using domain '" + domain + "'");
329
330 if (metaData.isKeyDomain("defaults")) {
331 final Map<String, String> keysection = metaData.getKeyDomain("defaults");
332
333 for (Map.Entry entry : keysection.entrySet()) {
334 final String key = entry.getKey().toString();
335 final String value = entry.getValue().toString();
336
337 defaults.setOption(domain, key, value);
338 }
339 }
340
341 if (metaData.isKeyDomain("formatters")) {
342 final Map<String, String> keysection = metaData.getKeyDomain("formatters");
343
344 for (Map.Entry entry : keysection.entrySet()) {
345 final String key = entry.getKey().toString();
346 final String value = entry.getValue().toString();
347
348 defaults.setOption("formatter", key, value);
349 }
350 }
351
352 if (metaData.isKeyDomain("icons")) {
353 final Map<String, String> keysection = metaData.getKeyDomain("icons");
354
355 for (Map.Entry entry : keysection.entrySet()) {
356 final String key = entry.getKey().toString();
357 final String value = entry.getValue().toString();
358
359 defaults.setOption("icon", key, value);
360 }
361 }
362 }
363
364
365
366 /**
367 * Update provides list.
368 */
369 private void updateProvides() {
370 // Remove us from any existing provides lists.
/*
P/P * Method: void updateProvides()
*
* Preconditions:
* this.metaData != null
* this.provides != null
* (soft) init'ed(com/dmdirc/plugins/PluginManager.me)
*
* Presumptions:
* getPluginManager(...).services != null
* java.util.Iterator:next(...)@371 != null
* java.util.Iterator:next(...)@379 != null
* service.serviceproviders@371 != null
* service.serviceproviders@385 != null
*
* Postconditions:
* init'ed(com/dmdirc/plugins/PluginManager.me)
* java.lang.StringBuilder:toString(...)._tainted == 0
* init'ed(new HashMap(PluginManager#2) num objects)
* init'ed(new Hashtable(PluginManager#1) num objects)
* init'ed(new PluginClassLoader(getSubClassLoader#1) num objects)
* init'ed(new PluginClassLoader(getSubClassLoader#1).pluginInfo)
* init'ed(new PluginManager(getPluginManager#1) num objects)
* init'ed(new PluginManager(getPluginManager#1).knownPlugins)
* init'ed(new PluginManager(getPluginManager#1).myDir)
* init'ed(new PluginManager(getPluginManager#1).services)
*
* Test Vectors:
* com.dmdirc.util.ConfigFile:getFlatDomain(...)@377: Addr_Set{null}, Inverse{null}
* java.lang.String:equalsIgnoreCase(...)@384: {1}, {0}
* java.util.Iterator:hasNext(...)@371: {0}, {1}
* java.util.Iterator:hasNext(...)@379: {0}, {1}
*/
371 for (Service service : provides) {
372 service.delProvider(this);
373 }
374 provides.clear();
375
376 // Get services provided by this plugin
377 final List<String> providesList = metaData.getFlatDomain("provides");
378 if (providesList != null) {
379 for (String item : providesList) {
380 final String[] bits = item.split(" ");
381 final String name = bits[0];
382 final String type = (bits.length > 1) ? bits[1] : "misc";
383
384 if (!name.equalsIgnoreCase("any") && !type.equalsIgnoreCase("export")) {
385 final Service service = PluginManager.getPluginManager().getService(type, name, true);
386 service.addProvider(this);
387 provides.add(service);
388 }
389 }
390 }
391
392 updateExports();
393 }
394
395 /**
396 * Called when the plugin is updated using the updater.
397 * Reloads metaData and updates the list of files.
398 */
399 public void pluginUpdated() {
400 try {
401 // Force a new resourcemanager just incase.
/*
P/P * Method: void pluginUpdated()
*
* Preconditions:
* (soft) init'ed(this.metaData)
* (soft) this.myClasses != null
* (soft) this.url != null
*
* Presumptions:
* com.dmdirc.util.resourcemanager.ResourceManager:getResourcesStartingWith(...)@405 != null
* java.util.Iterator:next(...)@405 != null
*
* Postconditions:
* this.metaData == One-of{&new ConfigFile(getConfigFile#1), &new ConfigFile(getMigratedConfigFile#1), null, old this.metaData}
* init'ed(this.metaData)
* possibly_updated(this.migrated)
* possibly_updated(this.myResourceManager)
* new ConfigFile(getConfigFile#1) num objects <= 1
* new ConfigFile(getMigratedConfigFile#1) num objects <= 1
*
* Test Vectors:
* java.lang.String:matches(...)@407: {0}, {1}
* java.util.Iterator:hasNext(...)@405: {0}, {1}
*/
402 final ResourceManager res = getResourceManager(true);
403
404 myClasses.clear();
405 for (final String classfilename : res.getResourcesStartingWith("")) {
406 String classname = classfilename.replace('/', '.');
407 if (classname.matches("^.*\\.class$")) {
408 classname = classname.replaceAll("\\.class$", "");
409 myClasses.add(classname);
410 }
411 }
412 updateMetaData();
413 updateProvides();
414 getDefaults();
415 } catch (IOException ioe) {
416 }
417 }
418
419 /**
420 * Check if this plugin was migrated or not.
421 *
422 * @return true if the plugins config file was a plugin.info not a plugin.config
423 */
424 public boolean isMigrated() {
/*
P/P * Method: bool isMigrated()
*
* Preconditions:
* init'ed(this.migrated)
*
* Postconditions:
* return_value == this.migrated
* init'ed(return_value)
*/
425 return migrated;
426 }
427
428 /**
429 * Try to reload the metaData from the plugin.config file.
430 * If this fails, the old data will be used still.
431 *
432 * @return true if metaData was reloaded ok, else false.
433 */
434 private boolean updateMetaData() {
435 // Force a new resourcemanager just incase.
436 try {
/*
P/P * Method: bool updateMetaData()
*
* Preconditions:
* (soft) init'ed(this.myResourceManager)
* (soft) this.url != null
*
* Postconditions:
* init'ed(return_value)
* this.metaData == One-of{&new ConfigFile(getConfigFile#1), &new ConfigFile(getMigratedConfigFile#1), null, old this.metaData}
* possibly_updated(this.migrated)
* init'ed(this.myResourceManager)
* new ConfigFile(getConfigFile#1) num objects <= 1
* new ConfigFile(getMigratedConfigFile#1) num objects <= 1
*/
437 final ResourceManager res = getResourceManager(true);
438 final ConfigFile newMetaData = getConfigFile();
439 if (newMetaData != null) {
440 metaData = newMetaData;
441 return true;
442 }
443 } catch (IOException ioe) { }
444
445 return false;
446 }
447
448 /**
449 * Get the contents of requirementsError
450 *
451 * @return requirementsError
452 */
453 public String getRequirementsError() {
/*
P/P * Method: String getRequirementsError()
*
* Preconditions:
* init'ed(this.requirementsError)
*
* Postconditions:
* return_value == this.requirementsError
* init'ed(return_value)
*/
454 return requirementsError;
455 }
456
457 /**
458 * Gets a resource manager for this plugin
459 *
460 * @return The resource manager for this plugin
461 * @throws IOException if there is any problem getting a ResourceManager for this plugin
462 */
463 public synchronized ResourceManager getResourceManager() throws IOException {
/*
P/P * Method: ResourceManager getResourceManager()
*
* Preconditions:
* init'ed(this.myResourceManager)
* (soft) this.url != null
*
* Postconditions:
* init'ed(return_value)
* this.myResourceManager == return_value
*/
464 return getResourceManager(false);
465 }
466
467 /**
468 * Get the resource manager for this plugin
469 *
470 * @return The resource manager for this plugin
471 * @param forceNew Force a new resource manager rather than using the old one.
472 * @throws IOException if there is any problem getting a ResourceManager for this plugin
473 * @since 0.6
474 */
475 public synchronized ResourceManager getResourceManager(final boolean forceNew) throws IOException {
/*
P/P * Method: ResourceManager getResourceManager(bool)
*
* Preconditions:
* init'ed(this.myResourceManager)
* (soft) this.url != null
*
* Postconditions:
* init'ed(return_value)
* this.myResourceManager == return_value
*
* Test Vectors:
* forceNew: {0}, {1}
*/
476 if (myResourceManager == null || forceNew) {
477 myResourceManager = ResourceManager.getResourceManager("jar://"+getFullFilename());
478
479 // Clear the resourcemanager in 10 seconds to stop us holding the file open
/*
P/P * Method: void com.dmdirc.plugins.PluginInfo$1(PluginInfo)
*/
480 new Timer(filename+"-resourcemanagerTimer").schedule(new TimerTask(){
481 /** {@inheritDoc} */
482 @Override
483 public void run() {
/*
P/P * Method: void run()
*/
484 myResourceManager = null;
485 }
486 }, 10000);
487 }
488 return myResourceManager;
489 }
490
491 /**
492 * Checks to see if the minimum version requirement of the plugin is
493 * satisfied.
494 * If either version is non-positive, the test passes.
495 * On failure, the requirementsError field will contain a user-friendly
496 * error message.
497 *
498 * @param desired The desired minimum version of DMDirc.
499 * @param actual The actual current version of DMDirc.
500 * @return True if the test passed, false otherwise
501 */
502 protected boolean checkMinimumVersion(final String desired, final int actual) {
503 int idesired;
504
/*
P/P * Method: bool checkMinimumVersion(String, int)
*
* Preconditions:
* desired != null
*
* Postconditions:
* init'ed(return_value)
* this.requirementsError == One-of{old this.requirementsError, &"'minversion' is a non-integer", &"Plugin is for a newer version of DMDirc"}
*
* Test Vectors:
* actual: {-231..0}, {1..232-2}
* java.lang.Integer:parseInt(...)@510: {-231..0}, {2..232-1}
* java.lang.String:isEmpty(...)@505: {0}, {1}
*/
505 if (desired.isEmpty()) {
506 return true;
507 }
508
509 try {
510 idesired = Integer.parseInt(desired);
511 } catch (NumberFormatException ex) {
512 requirementsError = "'minversion' is a non-integer";
513 return false;
514 }
515
516 if (actual > 0 && idesired > 0 && actual < idesired) {
517 requirementsError = "Plugin is for a newer version of DMDirc";
518 return false;
519 } else {
520 return true;
521 }
522 }
523
524 /**
525 * Checks to see if the maximum version requirement of the plugin is
526 * satisfied.
527 * If either version is non-positive, the test passes.
528 * If the desired version is empty, the test passes.
529 * On failure, the requirementsError field will contain a user-friendly
530 * error message.
531 *
532 * @param desired The desired maximum version of DMDirc.
533 * @param actual The actual current version of DMDirc.
534 * @return True if the test passed, false otherwise
535 */
536 protected boolean checkMaximumVersion(final String desired, final int actual) {
537 int idesired;
538
/*
P/P * Method: bool checkMaximumVersion(String, int)
*
* Preconditions:
* desired != null
*
* Postconditions:
* init'ed(return_value)
* this.requirementsError == One-of{old this.requirementsError, &"'maxversion' is a non-integer", &"Plugin is for an older version of DMDirc"}
*
* Test Vectors:
* actual: {-231..0}, {2..232-1}
* java.lang.Integer:parseInt(...)@544: {-231..0}, {1..232-2}
* java.lang.String:isEmpty(...)@539: {0}, {1}
*/
539 if (desired.isEmpty()) {
540 return true;
541 }
542
543 try {
544 idesired = Integer.parseInt(desired);
545 } catch (NumberFormatException ex) {
546 requirementsError = "'maxversion' is a non-integer";
547 return false;
548 }
549
550 if (actual > 0 && idesired > 0 && actual > idesired) {
551 requirementsError = "Plugin is for an older version of DMDirc";
552 return false;
553 } else {
554 return true;
555 }
556 }
557
558 /**
559 * Checks to see if the OS requirements of the plugin are satisfied.
560 * If the desired string is empty, the test passes.
561 * Otherwise it is used as one to three colon-delimited regular expressions,
562 * to test the name, version and architecture of the OS, respectively.
563 * On failure, the requirementsError field will contain a user-friendly
564 * error message.
565 *
566 * @param desired The desired OS requirements
567 * @param actualName The actual name of the OS
568 * @param actualVersion The actual version of the OS
569 * @param actualArch The actual architecture of the OS
570 * @return True if the test passes, false otherwise
571 */
572 protected boolean checkOS(final String desired, final String actualName, final String actualVersion, final String actualArch) {
/*
P/P * Method: bool checkOS(String, String, String, String)
*
* Preconditions:
* desired != null
* (soft) actualName != null
*
* Postconditions:
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* java.lang.StringBuilder:toString(...)._tainted == 0
* init'ed(return_value)
* this.requirementsError == One-of{old this.requirementsError, &java.lang.StringBuilder:toString(...)}
*
* Test Vectors:
* java.lang.String:isEmpty(...)@573: {0}, {1}
* java.lang.String:matches(...)@579: {1}, {0}
*/
573 if (desired.isEmpty()) {
574 return true;
575 }
576
577 final String[] desiredParts = desired.split(":");
578
579 if (!actualName.toLowerCase().matches(desiredParts[0])) {
580 requirementsError = "Invalid OS. (Wanted: '" + desiredParts[0] + "', actual: '" + actualName + "')";
581 return false;
582 } else if (desiredParts.length > 1 && !actualVersion.toLowerCase().matches(desiredParts[1])) {
583 requirementsError = "Invalid OS version. (Wanted: '" + desiredParts[1] + "', actual: '" + actualVersion + "')";
584 return false;
585 } else if (desiredParts.length > 2 && !actualArch.toLowerCase().matches(desiredParts[2])) {
586 requirementsError = "Invalid OS architecture. (Wanted: '" + desiredParts[2] + "', actual: '" + actualArch + "')";
587 return false;
588 }
589
590 return true;
591 }
592
593 /**
594 * Checks to see if the UI requirements of the plugin are satisfied.
595 * If the desired string is empty, the test passes.
596 * Otherwise it is used as a regular expressions against the package of the
597 * UIController to test what UI is currently in use.
598 * On failure, the requirementsError field will contain a user-friendly
599 * error message.
600 *
601 * @param desired The desired UI requirements
602 * @param actual The package of the current UI in use.
603 * @return True if the test passes, false otherwise
604 */
605 protected boolean checkUI(final String desired, final String actual) {
/*
P/P * Method: bool checkUI(String, String)
*
* Preconditions:
* desired != null
* (soft) actual != null
*
* Postconditions:
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* init'ed(return_value)
* this.requirementsError == One-of{old this.requirementsError, &java.lang.StringBuilder:toString(...)}
*
* Test Vectors:
* java.lang.String:isEmpty(...)@606: {0}, {1}
* java.lang.String:matches(...)@610: {1}, {0}
*/
606 if (desired.isEmpty()) {
607 return true;
608 }
609
610 if (!actual.toLowerCase().matches(desired)) {
611 requirementsError = "Invalid UI. (Wanted: '" + desired + "', actual: '" + actual + "')";
612 return false;
613 }
614 return true;
615 }
616
617 /**
618 * Checks to see if the file requirements of the plugin are satisfied.
619 * If the desired string is empty, the test passes.
620 * Otherwise it is passed to File.exists() to see if the file is valid.
621 * Multiple files can be specified by using a "," to separate. And either/or
622 * files can be specified using a "|" (eg /usr/bin/bash|/bin/bash)
623 * If the test fails, the requirementsError field will contain a
624 * user-friendly error message.
625 *
626 * @param desired The desired file requirements
627 * @return True if the test passes, false otherwise
628 */
629 protected boolean checkFiles(final String desired) {
/*
P/P * Method: bool checkFiles(String)
*
* Preconditions:
* desired != null
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == 0
* return_value == 1
* this.requirementsError == One-of{old this.requirementsError, &java.lang.StringBuilder:toString(...)}
*
* Test Vectors:
* java.lang.String:isEmpty(...)@630: {0}, {1}
*/
630 if (desired.isEmpty()) {
631 return true;
632 }
633
634 for (String files : desired.split(",")) {
635 final String[] filelist = files.split("\\|");
636 boolean foundFile = false;
637 for (String file : filelist) {
638 if ((new File(file)).exists()) {
639 foundFile = true;
640 break;
641 }
642 }
643 if (!foundFile) {
644 requirementsError = "Required file '"+files+"' not found";
645 return false;
646 }
647 }
648 return true;
649 }
650
651 /**
652 * Checks to see if the plugin requirements of the plugin are satisfied.
653 * If the desired string is empty, the test passes.
654 * Plugins should be specified as:
655 * plugin1[:minversion[:maxversion]],plugin2[:minversion[:maxversion]]
656 * Plugins will be attempted to be loaded if not loaded, else the test will
657 * fail if the versions don't match, or the plugin isn't known.
658 * If the test fails, the requirementsError field will contain a
659 * user-friendly error message.
660 *
661 * @param desired The desired file requirements
662 * @return True if the test passes, false otherwise
663 */
664 protected boolean checkPlugins(final String desired) {
/*
P/P * Method: bool checkPlugins(String)
*
* Preconditions:
* desired != null
*
* Postconditions:
* possibly_updated(com/dmdirc/plugins/PluginManager.me)
* java.lang.StringBuilder:toString(...)._tainted == 0
* return_value == 1
* this.requirementsError == One-of{old this.requirementsError, &java.lang.StringBuilder:toString(...)}
* new HashMap(PluginManager#2) num objects == 0
* new Hashtable(PluginManager#1) num objects == 0
* new PluginClassLoader(getSubClassLoader#1) num objects == 0
* new PluginClassLoader(getSubClassLoader#1).pluginInfo == null
* new PluginManager(getPluginManager#1) num objects == 0
* init'ed(new PluginManager(getPluginManager#1).knownPlugins)
* ...
*
* Test Vectors:
* java.lang.String:isEmpty(...)@665: {0}, {1}
*/
665 if (desired.isEmpty()) {
666 return true;
667 }
668
669 for (String plugin : desired.split(",")) {
670 final String[] data = plugin.split(":");
671 final PluginInfo pi = PluginManager.getPluginManager().getPluginInfoByName(data[0]);
672 if (pi == null) {
673 requirementsError = "Required plugin '"+data[0]+"' was not found";
674 return false;
675 } else {
676 if (data.length > 1) {
677 // Check plugin minimum version matches.
678 if (pi.getVersion().compareTo(new Version(data[1])) < 0) {
679 requirementsError = "Plugin '"+data[0]+"' is too old (Required Version: "+data[1]+", Actual Version: "+pi.getVersion()+")";
680 return false;
681 } else {
682 if (data.length > 2) {
683 // Check plugin maximum version matches.
684 if (pi.getVersion().compareTo(new Version(data[2])) > 0) {
685 requirementsError = "Plugin '"+data[0]+"' is too new (Required Version: "+data[2]+", Actual Version: "+pi.getVersion()+")";
686 return false;
687 }
688 }
689 }
690 }
691 }
692 }
693 return true;
694 }
695
696 /**
697 * Are the requirements for this plugin met?
698 *
699 * @param preliminary Is this a preliminary check?
700 * @return true/false (Actual error if false is in the requirementsError field)
701 */
702 public boolean checkRequirements(final boolean preliminary) {
/*
P/P * Method: bool checkRequirements(bool)
*
* Preconditions:
* init'ed(this.metaData)
*
* Presumptions:
* java.lang.System:getProperty(...)@720 != null
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == 0
* init'ed(return_value)
* this.requirementsError == One-of{old this.requirementsError, &java.lang.StringBuilder:toString(...)}
*
* Test Vectors:
* preliminary: {1}, {0}
* this.metaData: Inverse{null}, Addr_Set{null}
* checkPlugins(...)@720: {0}, {1}
* checkServices(...)@720: {1}, {0}
*/
703 if (metaData == null) {
704 // No meta-data, so no requirements.
705 return true;
706 }
707
708 /* final String uiPackage;
709 if (Main.getUI().getClass().getPackage() != null) {
710 uiPackage = Main.getUI().getClass().getPackage().getName();
711 } else {
712 final String uiController = Main.getUI().getClass().getName();
713 if (uiController.lastIndexOf('.') >= 0) {
714 uiPackage = uiController.substring(0,uiController.lastIndexOf('.'));
715 } else {
716 uiPackage = uiController;
717 }
718 } */
719
720 if (/*!checkMinimumVersion(getMinVersion(), Main.SVN_REVISION) ||
721 !checkMaximumVersion(getMaxVersion(), Main.SVN_REVISION) ||*/
722 !checkOS(getKeyValue("requires", "os", ""), System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")) ||
723 !checkFiles(getKeyValue("requires", "files", "")) ||
724 (!preliminary && !checkPlugins(getKeyValue("requires", "plugins", ""))) ||
725 (!preliminary && !checkServices(metaData.getFlatDomain("required-services")))
726 ) {
727 return false;
728 }
729
730 // All requirements passed, woo \o
731 return true;
732 }
733
734 /**
735 * Check if the services required by this plugin are available.
736 *
737 * @param services Required services
738 * @return true if all services are available
739 */
740 private boolean checkServices(final List<String> services) {
/*
P/P * Method: bool checkServices(List)
*
* Preconditions:
* (soft) init'ed(com/dmdirc/plugins/PluginManager.me)
*
* Presumptions:
* getPluginManager(...).services != null
* java.util.Iterator:next(...)@743 != null
* java.util.List:get(...)@755 != null
*
* Postconditions:
* init'ed(com/dmdirc/plugins/PluginManager.me)
* java.lang.StringBuilder:toString(...)._tainted == 0
* init'ed(return_value)
* init'ed(new HashMap(PluginManager#2) num objects)
* init'ed(new Hashtable(PluginManager#1) num objects)
* init'ed(new PluginClassLoader(getSubClassLoader#1) num objects)
* possibly_updated(new PluginClassLoader(getSubClassLoader#1).pluginInfo)
* init'ed(new PluginManager(getPluginManager#1) num objects)
* init'ed(new PluginManager(getPluginManager#1).knownPlugins)
* init'ed(new PluginManager(getPluginManager#1).myDir)
* ...
*
* Test Vectors:
* services: Addr_Set{null}, Inverse{null}
* java.lang.String:equalsIgnoreCase(...)@751: {0}, {1}
* java.util.Iterator:hasNext(...)@743: {0}, {1}
* java.util.List:size(...)@741: {1..232-1}, {-231..0}
*/
741 if (services == null || services.size() < 1) { return true; }
742
743 for (String requirement : services) {
744 boolean available = false;
745 final String[] bits = requirement.split(" ");
746 final String name = bits[0];
747 final String type = (bits.length > 1) ? bits[1] : "misc";
748
749 // System.out.println(toString()+" Looking for: "+requirement);
750 Service service = null;
751 if (name.equalsIgnoreCase("any")) {
752 final List<Service> serviceList = PluginManager.getPluginManager().getServicesByType(type);
753 if (serviceList.size() > 0) {
754 // Default to the first Service in the list
755 service = serviceList.get(0);
756 if (serviceList.size() > 1) {
757 // Check to see if any of the others are already active
758 for (Service serv : serviceList) {
759 if (service.isActive()) {
760 // Already active, abort.
761 available = true;
762 }
763 }
764 }
765 }
766 } else {
767 service = PluginManager.getPluginManager().getService(type, name, false);
768 }
769 // System.out.println("\tSatisfied by: "+service+" "+(PluginInfo)service.getActiveProvider());
770 if (service != null) {
771 available = service.activate();
772 }
773
774 if (!available) { return false; }
775 }
776
777 return true;
778 }
779
780 /**
781 * Is this provider active at this time.
782 *
783 * @return true if the provider is able to provide its services
784 */
785 @Override
/*
P/P * Method: bool isActive()
*
* Preconditions:
* init'ed(this.plugin)
* (soft) init'ed(this.tempLoaded)
*
* Postconditions:
* init'ed(return_value)
*/
786 public final boolean isActive() { return isLoaded(); }
787
788 /** Activate the services. */
789 @Override
/*
P/P * Method: void activateServices()
*
* Preconditions:
* (soft) init'ed(this.plugin)
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == 0
* this.isLoading == old this.isLoading
* this.lastError == old this.lastError
* this.tempLoaded == old this.tempLoaded
*/
790 public void activateServices() { loadPlugin(); }
791
792 /** {@inheritDoc} */
793 @Override
/*
P/P * Method: String getProviderName()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* return_value == &java.lang.StringBuilder:toString(...)
*/
794 public String getProviderName() { return "Plugin: "+getNiceName()+" ("+getName()+" / "+getFilename()+")"; }
795
796 /**
797 * Get a list of services provided by this provider.
798 *
799 * @return A list of services provided by this provider.
800 */
801 @Override
802 public List<Service> getServices() {
/*
P/P * Method: List getServices()
*
* Postconditions:
* return_value == &new ArrayList(getServices#1)
* new ArrayList(getServices#1) num objects == 1
*/
803 return new ArrayList<Service>(provides);
804 }
805
806 /**
807 * Is this plugin loaded?
808 *
809 * @return True if the plugin is currently (non-temporarily) loaded, false
810 * otherwise
811 */
812 public boolean isLoaded() {
/*
P/P * Method: bool isLoaded()
*
* Preconditions:
* init'ed(this.plugin)
* (soft) init'ed(this.tempLoaded)
*
* Postconditions:
* init'ed(return_value)
*/
813 return (plugin != null) && !tempLoaded;
814 }
815
816 /**
817 * Is this plugin temporarily loaded?
818 *
819 * @return True if this plugin is currently temporarily loaded, false
820 * otherwise
821 */
822 public boolean isTempLoaded() {
/*
P/P * Method: bool isTempLoaded()
*
* Preconditions:
* init'ed(this.plugin)
* (soft) init'ed(this.tempLoaded)
*
* Postconditions:
* init'ed(return_value)
*/
823 return (plugin != null) && tempLoaded;
824 }
825
826 /**
827 * Load entire plugin.
828 * This loads all files in the jar immediately.
829 *
830 * @throws PluginException if there is an error with the resourcemanager
831 */
832 private void loadEntirePlugin() throws PluginException {
833 // Load the main "Plugin" from the jar
/*
P/P * Method: void loadEntirePlugin()
*
* Preconditions:
* (soft) init'ed(this.plugin)
* this.myClasses != null
* (soft) init'ed(com/dmdirc/plugins/GlobalClassLoader.me)
* (soft) init'ed(this.classloader)
*
* Postconditions:
* com/dmdirc/plugins/GlobalClassLoader.me == old com/dmdirc/plugins/GlobalClassLoader.me
* java.lang.StringBuilder:toString(...)._tainted == 0
* this.classloader == old this.classloader
* this.isLoading == old this.isLoading
* this.lastError == old this.lastError
* this.plugin == old this.plugin
* this.plugin.domainSet == old this.plugin.domainSet
* this.plugin.myDomain == old this.plugin.myDomain
* this.tempLoaded == old this.tempLoaded
* new GlobalClassLoader(getGlobalClassLoader#1) num objects == undefined
* ...
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@837: {0}, {1}
*/
834 loadPlugin();
835
836 // Now load all the rest.
837 for (String classname : myClasses) {
838 loadClass(classname);
839 }
840 }
841
842 /**
843 * Try to Load the plugin files temporarily.
844 */
845 public void loadPluginTemp() {
/*
P/P * Method: void loadPluginTemp()
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == 0
* this.isLoading == old this.isLoading
* this.lastError == old this.lastError
* this.tempLoaded == 1
*/
846 tempLoaded = true;
847 loadPlugin();
848 }
849
850 /**
851 * Load any required plugins
852 */
853 public void loadRequired() {
/*
P/P * Method: void loadRequired()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* possibly_updated(com/dmdirc/plugins/PluginManager.me)
* java.lang.StringBuilder:toString(...)._tainted == 0
* new HashMap(PluginManager#2) num objects == 0
* new Hashtable(PluginManager#1) num objects == 0
* new PluginClassLoader(getSubClassLoader#1) num objects == 0
* possibly_updated(new PluginClassLoader(getSubClassLoader#1).pluginInfo)
* new PluginManager(getPluginManager#1) num objects == 0
* init'ed(new PluginManager(getPluginManager#1).knownPlugins)
* init'ed(new PluginManager(getPluginManager#1).myDir)
* init'ed(new PluginManager(getPluginManager#1).services)
*/
854 final String required = getKeyValue("requires", "plugins", "");
855 for (String plugin : required.split(",")) {
856 final String[] data = plugin.split(":");
857 if (!data[0].trim().isEmpty()) {
858 final PluginInfo pi = PluginManager.getPluginManager().getPluginInfoByName(data[0]);
859
860 if (pi == null) {
861 return;
862 }
863 if (tempLoaded) {
864 pi.loadPluginTemp();
865 } else {
866 pi.loadPlugin();
867 }
868 }
869 }
870 }
871
872 /**
873 * Load the plugin files.
874 */
875 public void loadPlugin() {
/*
P/P * Method: void loadPlugin()
*
* Preconditions:
* init'ed(this.plugin)
* this.exports != null
* this.provides != null
* (soft) init'ed(com/dmdirc/plugins/GlobalClassLoader.me)
* (soft) init'ed(this.isLoading)
* (soft) init'ed(this.requirementsError)
* (soft) init'ed(this.tempLoaded)
* (soft) this.children != null
* (soft) this.metaData != null
*
* Presumptions:
* (soft) init'ed(com.dmdirc.actions.CoreActionType.PLUGIN_LOADED)
*
* Postconditions:
* com/dmdirc/logger/ProgramError.errorDir == One-of{old com/dmdirc/logger/ProgramError.errorDir, &new File(getErrorFile#1)}
* com/dmdirc/plugins/GlobalClassLoader.me == old com/dmdirc/plugins/GlobalClassLoader.me
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* java.lang.StringBuilder:toString(...)._tainted == 0
* this.classloader == One-of{old this.classloader, null}
* init'ed(this.isLoading)
* possibly_updated(this.lastError)
* this.plugin == One-of{old this.plugin, null}
* init'ed(this.plugin)
* this.plugin.domainSet == old this.plugin.domainSet
* ...
*
* Test Vectors:
* !(this.plugin == null) & this.tempLoaded != 0: {0}, {1}
* !(this.plugin == null) & this.tempLoaded == 0: {0}, {1}
* (!(this.plugin == null) & this.tempLoaded == 0) | this.plugin == null: {0}, {1}
* this.isLoading: {0}, {1}
* this.tempLoaded: {1}, {0}
*/
876 updateProvides();
877 if (!checkRequirements(isTempLoaded() || tempLoaded)) {
878 lastError = "Unable to loadPlugin, all requirements not met. ("+requirementsError+")";
879 return;
880 }
881 if (isTempLoaded()) {
882 tempLoaded = false;
883 loadRequired();
884
885 try {
886 plugin.onLoad();
887 } catch (Throwable e) {
888 lastError = "Error in onLoad for "+getName()+":"+e.getMessage();
889 Logger.userError(ErrorLevel.MEDIUM, lastError, e);
890 unloadPlugin();
891 }
892 } else {
893 if (isLoaded() || metaData == null || isLoading) {
894 lastError = "Not Loading: ("+isLoaded()+"||"+(metaData == null)+"||"+isLoading+")";
895 return;
896 }
897 isLoading = true;
898 loadRequired();
899 loadClass(getMainClass());
900 if (isLoaded()) {
901 ActionManager.processEvent(CoreActionType.PLUGIN_LOADED, null, this);
902 }
903 isLoading = false;
904 }
905 }
906
907 /**
908 * Add the given Plugin as a child of this plugin.
909 *
910 * @param child Child to add
911 */
912 public void addChild(final PluginInfo child) {
/*
P/P * Method: void addChild(PluginInfo)
*
* Preconditions:
* this.children != null
*/
913 children.add(child);
914 }
915
916 /**
917 * Remove the given Plugin as a child of this plugin.
918 *
919 * @param child Child to remove
920 */
921 public void delChild(final PluginInfo child) {
/*
P/P * Method: void delChild(PluginInfo)
*
* Preconditions:
* this.children != null
*/
922 children.remove(child);
923 }
924
925 /**
926 * Load the given classname.
927 *
928 * @param classname Class to load
929 */
930 private void loadClass(final String classname) {
931 try {
/*
P/P * Method: void loadClass(String)
*
* Preconditions:
* (soft) classname != null
* (soft) init'ed(com/dmdirc/plugins/GlobalClassLoader.me)
* (soft) init'ed(this.classloader)
* (soft) init'ed(this.tempLoaded)
* (soft) this.children != null
* (soft) this.classloader.pluginInfo != null
* (soft) init'ed(this.metaData)
* (soft) this.provides != null
*
* Presumptions:
* java.util.logging.Logger:getLogger(...)@52 != null
*
* Postconditions:
* possibly_updated(com/dmdirc/logger/ProgramError.errorDir)
* init'ed(com/dmdirc/plugins/GlobalClassLoader.me)
* possibly_updated(com/dmdirc/plugins/PluginManager.me)
* java.lang.StringBuilder:toString(...)._tainted == 0
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* possibly_updated(java.lang.StringBuilder:toString(...)._tainted)
* possibly_updated(this...myResourceManager)
* possibly_updated(this.classloader)
* possibly_updated(this.lastError)
* possibly_updated(this.myResourceManager)
* ...
*
* Test Vectors:
* this.classloader: Inverse{null}, Addr_Set{null}
* com.dmdirc.config.prefs.validator.ValidationResponse:isFailure(...)@964: {1}, {0}
* java.lang.String:equals(...)@959: {0}, {1}
* java.lang.String:isEmpty(...)@933: {0}, {1}
*/
932 if (classloader == null) {
933 if (getKeyValue("requires", "parent", "").isEmpty()) {
934 classloader = new PluginClassLoader(this);
935 } else {
936 final String parentName = getKeyValue("requires", "parent", "");
937 final PluginInfo pi = PluginManager.getPluginManager().getPluginInfoByName(parentName);
938 if (pi == null) {
939 lastError = "Required parent '"+parentName+"' was not found";
940 return;
941 } else {
942 pi.addChild(this);
943 classloader = pi.getPluginClassLoader().getSubClassLoader(this);
944 }
945 }
946 }
947
948 // Don't reload a class if its already loaded.
949 if (classloader.isClassLoaded(classname, true)) {
950 lastError = "Classloader says we are already loaded.";
951 return;
952 }
953
954 final Class<?> c = classloader.loadClass(classname);
955 final Constructor<?> constructor = c.getConstructor(new Class[] {});
956
957 // Only try and construct the main class, anything else should be constructed
958 // by the plugin itself.
959 if (classname.equals(getMainClass())) {
960 final Object temp = constructor.newInstance(new Object[] {});
961
962 if (temp instanceof Plugin) {
963 final ValidationResponse prerequisites = ((Plugin) temp).checkPrerequisites();
964 if (!prerequisites.isFailure()) {
965 plugin = (Plugin) temp;
966 LOGGER.finer(getName() + ": Setting domain 'plugin-" + getName() + "'");
967 plugin.setDomain("plugin-"+getName());
968 if (!tempLoaded) {
969 try {
970 plugin.onLoad();
971 } catch (Throwable e) {
972 lastError = "Error in onLoad for "+getName()+":"+e.getMessage();
973 Logger.userError(ErrorLevel.MEDIUM, lastError, e);
974 unloadPlugin();
975 }
976 }
977 } else {
978 if (!tempLoaded) {
979 lastError = "Prerequisites for plugin not met. ('"+filename+":"+getMainClass()+"' -> '"+prerequisites.getFailureReason()+"') ";
980 Logger.userError(ErrorLevel.LOW, lastError);
981 }
982 }
983 }
984 }
985 } catch (ClassNotFoundException cnfe) {
986 lastError = "Class not found ('"+filename+":"+classname+":"+classname.equals(getMainClass())+"') - "+cnfe.getMessage();
987 Logger.userError(ErrorLevel.LOW, lastError, cnfe);
988 } catch (NoSuchMethodException nsme) {
989 // Don't moan about missing constructors for any class thats not the main Class
990 lastError = "Constructor missing ('"+filename+":"+classname+":"+classname.equals(getMainClass())+"') - "+nsme.getMessage();
991 if (classname.equals(getMainClass())) {
992 Logger.userError(ErrorLevel.LOW, lastError, nsme);
993 }
994 } catch (IllegalAccessException iae) {
995 lastError = "Unable to access constructor ('"+filename+":"+classname+":"+classname.equals(getMainClass())+"') - "+iae.getMessage();
996 Logger.userError(ErrorLevel.LOW, lastError, iae);
997 } catch (InvocationTargetException ite) {
998 lastError = "Unable to invoke target ('"+filename+":"+classname+":"+classname.equals(getMainClass())+"') - "+ite.getMessage();
999 Logger.userError(ErrorLevel.LOW, lastError, ite);
1000 } catch (InstantiationException ie) {
1001 lastError = "Unable to instantiate plugin ('"+filename+":"+classname+":"+classname.equals(getMainClass())+"') - "+ie.getMessage();
1002 Logger.userError(ErrorLevel.LOW, lastError, ie);
1003 } catch (NoClassDefFoundError ncdf) {
1004 lastError = "Unable to instantiate plugin ('"+filename+":"+classname+":"+classname.equals(getMainClass())+"') - Unable to find class: " + ncdf.getMessage();
1005 Logger.userError(ErrorLevel.LOW, lastError, ncdf);
1006 } catch (VerifyError ve) {
1007 lastError = "Unable to instantiate plugin ('"+filename+":"+classname+":"+classname.equals(getMainClass())+"') - Incompatible: "+ve.getMessage();
1008 Logger.userError(ErrorLevel.LOW, lastError, ve);
1009 }
1010 }
1011
1012 /**
1013 * Unload the plugin if possible.
1014 */
1015 public void unloadPlugin() {
/*
P/P * Method: void unloadPlugin()
*
* Preconditions:
* (soft) this.plugin != null
* (soft) init'ed(this.tempLoaded)
* (soft) this.children != null
* (soft) init'ed(this.metaData)
* (soft) this.provides != null
*
* Postconditions:
* possibly_updated(java.lang.StringBuilder:toString(...)._tainted)
* this.classloader == One-of{old this.classloader, null}
* possibly_updated(this.lastError)
* this.plugin == One-of{old this.plugin, null}
* this.tempLoaded == One-of{old this.tempLoaded, 0}
* possibly_updated(new PluginClassLoader(getSubClassLoader#1) num objects)
* possibly_updated(new PluginClassLoader(getSubClassLoader#1).pluginInfo)
*/
1016 unloadPlugin(false);
1017 }
1018
1019 /**
1020 * Can this plugin be unloaded?
1021 * Will return false if:
1022 * - The plugin is persistent (all its classes are loaded into the global class loader)
1023 * - The plugin isn't currently loaded
1024 * - The metadata key "unloadable" is set to false, no or 0
1025 *
1026 * @return true if plugin can be unloaded
1027 */
1028 public boolean isUnloadable() {
/*
P/P * Method: bool isUnloadable()
*
* Preconditions:
* init'ed(this.metaData)
* (soft) init'ed(this.plugin)
* (soft) init'ed(this.tempLoaded)
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* !(this.plugin == null) & this.tempLoaded != 0: {0}, {1}
* !(this.plugin == null) & this.tempLoaded == 0: {1}, {0}
* (!(this.plugin == null) & this.tempLoaded != 0) | this.plugin == null: {0}, {1}
* this.tempLoaded: {0}, {1}
*/
1029 if (isPersistent() || (!isLoaded() && !isTempLoaded())) {
1030 return false;
1031 } else {
1032 final String unloadable = getKeyValue("metadata", "unloadable", "true");
1033 return (unloadable.equalsIgnoreCase("yes") || unloadable.equalsIgnoreCase("true") || unloadable.equalsIgnoreCase("1"));
1034 }
1035 }
1036
1037 /**
1038 * Unload the plugin if possible.
1039 *
1040 * @param parentUnloading is our parent already unloading? (if so, don't call delChild)
1041 */
1042 private void unloadPlugin(final boolean parentUnloading) {
/*
P/P * Method: void unloadPlugin(bool)
*
* Preconditions:
* (soft) this.plugin != null
* (soft) init'ed(this.tempLoaded)
* (soft) this.children != null
* (soft) init'ed(this.metaData)
* (soft) this.provides != null
*
* Presumptions:
* init'ed(com.dmdirc.actions.CoreActionType.PLUGIN_UNLOADED)
*
* Postconditions:
* com/dmdirc/logger/ProgramError.errorDir == One-of{old com/dmdirc/logger/ProgramError.errorDir, &new File(getErrorFile#1)}
* com/dmdirc/plugins/PluginManager.me == One-of{old com/dmdirc/plugins/PluginManager.me, &new PluginManager(getPluginManager#1)}
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* possibly_updated(java.lang.StringBuilder:toString(...)._tainted)
* this.classloader == One-of{old this.classloader, null}
* possibly_updated(this.lastError)
* this.plugin == One-of{old this.plugin, null}
* init'ed(this.plugin)
* init'ed(this.tempLoaded)
* 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
* ...
*
* Test Vectors:
* !(this.plugin == null) & this.tempLoaded != 0: {1}, {0}
* !(this.plugin == null) & this.tempLoaded == 0: {0}, {1}
* (!(this.plugin == null) & this.tempLoaded != 0) | this.plugin == null: {1}, {0}
* (!(this.plugin == null) & this.tempLoaded == 0) | this.plugin == null: {0}, {1}
* this.tempLoaded: {1}, {0}
* parentUnloading: {1}, {0}
* java.lang.String:isEmpty(...)@1050: {1}, {0}
* java.util.Iterator:hasNext(...)@1046: {0}, {1}
* java.util.Iterator:hasNext(...)@1067: {0}, {1}
*/
1043 if (isUnloadable()) {
1044 if (!isTempLoaded()) {
1045 // Unload all children
1046 for (PluginInfo child : children) {
1047 child.unloadPlugin(true);
1048 }
1049 // Delete ourself as a child of our parent.
1050 if (!parentUnloading && !getKeyValue("requires", "parent", "").isEmpty()) {
1051 final String parentName = getKeyValue("requires", "parent", "");
1052 final PluginInfo pi = PluginManager.getPluginManager().getPluginInfoByName(parentName);
1053 if (pi != null) {
1054 pi.delChild(this);
1055 classloader = pi.getPluginClassLoader().getSubClassLoader(this);
1056 }
1057 }
1058 // Now unload ourself
1059 try {
1060 plugin.onUnload();
1061 } catch (Exception e) {
1062 lastError = "Error in onUnload for "+getName()+":"+e+" - "+e.getMessage();
1063 Logger.userError(ErrorLevel.MEDIUM, lastError, e);
1064 e.printStackTrace();
1065 }
1066 ActionManager.processEvent(CoreActionType.PLUGIN_UNLOADED, null, this);
1067 for (Service service : provides) {
1068 service.delProvider(this);
1069 }
1070 provides.clear();
1071 }
1072 tempLoaded = false;
1073 plugin = null;
1074 classloader = null;
1075 }
1076 }
1077
1078 /**
1079 * Get the last Error
1080 *
1081 * @return last Error
1082 * @since 0.6
1083 */
/*
P/P * Method: String getLastError()
*
* Preconditions:
* init'ed(this.lastError)
*
* Postconditions:
* return_value == this.lastError
* init'ed(return_value)
*/
1084 public String getLastError() { return lastError; }
1085
1086 /**
1087 * Get the list of Classes
1088 *
1089 * @return Classes this plugin has
1090 */
1091 public List<String> getClassList() {
/*
P/P * Method: List getClassList()
*
* Preconditions:
* init'ed(this.myClasses)
*
* Postconditions:
* return_value == this.myClasses
* init'ed(return_value)
*/
1092 return myClasses;
1093 }
1094
1095 /**
1096 * Get the value of the given key from the given keysection, or fallback.
1097 *
1098 * @param section Section to look in
1099 * @param key Key to check
1100 * @param fallback Value to use if key doesn't exist.
1101 * @return Value of the key in the keysection, or the fallback if not present
1102 */
1103 public String getKeyValue(final String section, final String key, final String fallback) {
/*
P/P * Method: String getKeyValue(String, String, String)
*
* Preconditions:
* init'ed(this.metaData)
*
* Presumptions:
* com.dmdirc.util.ConfigFile:getKeyDomain(...)@1105 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* this.metaData: Addr_Set{null}, Inverse{null}
* com.dmdirc.util.ConfigFile:isKeyDomain(...)@1104: {0}, {1}
*/
1104 if (metaData != null && metaData.isKeyDomain(section)) {
1105 final Map<String, String> keysection = metaData.getKeyDomain(section);
1106 return keysection.containsKey(key) ? keysection.get(key) : fallback;
1107 }
1108
1109 return fallback;
1110 }
1111
1112 /**
1113 * Get the main Class
1114 *
1115 * @return Main Class to begin loading.
1116 */
/*
P/P * Method: String getMainClass()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1117 public String getMainClass() { return getKeyValue("metadata", "mainclass", ""); }
1118
1119 /**
1120 * Get the Plugin for this plugin.
1121 *
1122 * @return Plugin
1123 */
/*
P/P * Method: Plugin getPlugin()
*
* Preconditions:
* init'ed(this.plugin)
*
* Postconditions:
* return_value == this.plugin
* init'ed(return_value)
*/
1124 public Plugin getPlugin() { return plugin; }
1125
1126 /**
1127 * Get the PluginClassLoader for this plugin.
1128 *
1129 * @return PluginClassLoader
1130 */
/*
P/P * Method: PluginClassLoader getPluginClassLoader()
*
* Preconditions:
* init'ed(this.classloader)
*
* Postconditions:
* return_value == this.classloader
* init'ed(return_value)
*/
1131 protected PluginClassLoader getPluginClassLoader() { return classloader; }
1132
1133 /**
1134 * Get the plugin friendly version
1135 *
1136 * @return Plugin friendly Version
1137 */
/*
P/P * Method: String getFriendlyVersion()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* java.lang.String:valueOf(...)._tainted == 0
* init'ed(return_value)
*/
1138 public String getFriendlyVersion() { return getKeyValue("version", "friendly", String.valueOf(getVersion())); }
1139
1140 /**
1141 * Get the plugin version
1142 *
1143 * @return Plugin Version
1144 */
1145 public Version getVersion() {
/*
P/P * Method: Version getVersion()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* return_value == &new Version(getVersion#1)
* new Version(getVersion#1) num objects == 1
*/
1146 return new Version(getKeyValue("version", "number", "0"));
1147 }
1148
1149 /**
1150 * Get the id for this plugin on the addons site.
1151 * If a plugin has been submitted to addons.dmdirc.com, and plugin.config
1152 * contains a property addonid then this will return it.
1153 * This is used along with the version property to allow the auto-updater to
1154 * update the addon if the author submits a new version to the addons site.
1155 *
1156 * @return Addon Site ID number
1157 * -1 If not present
1158 * -2 If non-integer
1159 */
1160 public int getAddonID() {
1161 try {
/*
P/P * Method: int getAddonID()
*
* Preconditions:
* (soft) init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1162 return Integer.parseInt(getKeyValue("updates", "id", "-1"));
1163 } catch (NumberFormatException nfe) {
1164 return -2;
1165 }
1166 }
1167
1168 /**
1169 * Is this a persistent plugin?
1170 *
1171 * @return true if persistent, else false
1172 */
1173 public boolean isPersistent() {
/*
P/P * Method: bool isPersistent()
*
* Preconditions:
* init'ed(this.metaData)
*
* Presumptions:
* com.dmdirc.util.ConfigFile:getFlatDomain(...)@1175 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* this.metaData: Addr_Set{null}, Inverse{null}
* com.dmdirc.util.ConfigFile:isFlatDomain(...)@1174: {0}, {1}
*/
1174 if (metaData != null && metaData.isFlatDomain("persistent")) {
1175 final List<String> items = metaData.getFlatDomain("persistent");
1176 return items.contains("*");
1177 }
1178
1179 return false;
1180 }
1181
1182 /**
1183 * Does this plugin contain any persistent classes?
1184 *
1185 * @return true if this plugin contains any persistent classes, else false
1186 */
1187 public boolean hasPersistent() {
/*
P/P * Method: bool hasPersistent()
*
* Preconditions:
* init'ed(this.metaData)
*
* Presumptions:
* com.dmdirc.util.ConfigFile:getFlatDomain(...)@1189 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* this.metaData: Addr_Set{null}, Inverse{null}
* com.dmdirc.util.ConfigFile:isFlatDomain(...)@1188: {0}, {1}
*/
1188 if (metaData != null && metaData.isFlatDomain("persistent")) {
1189 final List<String> items = metaData.getFlatDomain("persistent");
1190 return !items.isEmpty();
1191 }
1192
1193 return false;
1194 }
1195
1196 /**
1197 * Get a list of all persistent classes in this plugin
1198 *
1199 * @return List of all persistent classes in this plugin
1200 */
1201 public List<String> getPersistentClasses() {
/*
P/P * Method: List getPersistentClasses()
*
* Preconditions:
* init'ed(this.metaData)
* (soft) this.url != null
*
* Presumptions:
* com.dmdirc.util.resourcemanager.ResourceManager:getResourcesStartingWith(...)@1208 != null
* java.util.Iterator:next(...)@1208 != null
*
* Postconditions:
* init'ed(return_value)
* possibly_updated(this.myResourceManager)
* new ArrayList(getPersistentClasses#1) num objects == 1
*
* Test Vectors:
* this.metaData: Addr_Set{null}, Inverse{null}
* com.dmdirc.util.ConfigFile:isFlatDomain(...)@1216: {0}, {1}
* java.lang.String:matches(...)@1209: {0}, {1}
*/
1202 final List<String> result = new ArrayList<String>();
1203
1204 if (isPersistent()) {
1205 try {
1206 ResourceManager res = getResourceManager();
1207
1208 for (final String filename : res.getResourcesStartingWith("")) {
1209 if (filename.matches("^.*\\.class$")) {
1210 result.add(filename.replaceAll("\\.class$", "").replace('/', '.'));
1211 }
1212 }
1213 } catch (IOException e) {
1214 // Jar no longer exists?
1215 }
1216 } else if (metaData != null && metaData.isFlatDomain("persistent")) {
1217 return metaData.getFlatDomain("persistent");
1218 }
1219
1220 return result;
1221 }
1222
1223 /**
1224 * Is this a persistent class?
1225 *
1226 * @param classname class to check persistence of
1227 * @return true if file (or whole plugin) is persistent, else false
1228 */
1229 public boolean isPersistent(final String classname) {
/*
P/P * Method: bool isPersistent(String)
*
* Preconditions:
* init'ed(this.metaData)
*
* Presumptions:
* com.dmdirc.util.ConfigFile:getFlatDomain(...)@1233 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* this.metaData: Addr_Set{null}, Inverse{null}
* com.dmdirc.util.ConfigFile:isFlatDomain(...)@1232: {0}, {1}
*/
1230 if (isPersistent()) {
1231 return true;
1232 } else if (metaData != null && metaData.isFlatDomain("persistent")) {
1233 final List<String> items = metaData.getFlatDomain("persistent");
1234 return items.contains(classname);
1235 } else {
1236 return false;
1237 }
1238 }
1239
1240 /**
1241 * Get the plugin Filename.
1242 *
1243 * @return Filename of plugin
1244 */
/*
P/P * Method: String getFilename()
*
* Postconditions:
* return_value == this.filename
* init'ed(return_value)
*/
1245 public String getFilename() { return filename; }
1246
1247 /**
1248 * Get the full plugin Filename (inc dirname)
1249 *
1250 * @return Filename of plugin
1251 */
/*
P/P * Method: String getFullFilename()
*
* Preconditions:
* this.url != null
*
* Postconditions:
* init'ed(return_value)
*/
1252 public String getFullFilename() { return url.getPath(); }
1253
1254 /**
1255 * Retrieves the path to this plugin relative to the main plugin directory,
1256 * if appropriate.
1257 *
1258 * @return A relative path to the plugin if it is situated under the main
1259 * plugin directory, or an absolute path otherwise.
1260 */
1261 public String getRelativeFilename() {
/*
P/P * Method: String getRelativeFilename()
*
* Preconditions:
* init'ed(com/dmdirc/plugins/PluginManager.me)
* this.url != null
*
* Presumptions:
* getPluginManager(...).myDir != null
* getPluginManager(...)@1262 init'ed
* java.net.URL:getPath(...)@1252 != null
*
* Postconditions:
* com/dmdirc/plugins/PluginManager.me == One-of{old com/dmdirc/plugins/PluginManager.me, &new PluginManager(getPluginManager#1)}
* com/dmdirc/plugins/PluginManager.me != null
* java.lang.String:substring(...)._tainted == 0
* java.lang.StringBuilder:toString(...)._tainted == 0
* init'ed(return_value)
* new HashMap(PluginManager#2) num objects <= 1
* new Hashtable(PluginManager#1) num objects == new HashMap(PluginManager#2) num objects
* new PluginManager(getPluginManager#1) num objects == new HashMap(PluginManager#2) num objects
* new PluginClassLoader(getSubClassLoader#1) num objects == undefined
* new PluginClassLoader(getSubClassLoader#1) num objects == 0, if init'ed
* ...
*
* Test Vectors:
* java.lang.String:startsWith(...)@1263: {0}, {1}
*/
1262 final String dir = PluginManager.getPluginManager().getDirectory();
1263 return getFullFilename().startsWith(dir) ? getFullFilename().substring(dir.length()) : getFullFilename();
1264 }
1265
1266 /**
1267 * Get the plugin Author.
1268 *
1269 * @return Author of plugin
1270 */
/*
P/P * Method: String getAuthor()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1271 public String getAuthor() { return getKeyValue("metadata", "author", ""); }
1272
1273 /**
1274 * Get the plugin Description.
1275 *
1276 * @return Description of plugin
1277 */
/*
P/P * Method: String getDescription()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1278 public String getDescription() { return getKeyValue("metadata", "description", ""); }
1279
1280 /**
1281 * Get the minimum dmdirc version required to run the plugin.
1282 *
1283 * @return minimum dmdirc version required to run the plugin.
1284 */
1285 public String getMinVersion() {
/*
P/P * Method: String getMinVersion()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* return_value in Addr_Set{null,&""}
*
* Test Vectors:
* java.lang.String:isEmpty(...)@1287: {1}, {0}
*/
1286 final String requiredVersion = getKeyValue("requires", "dmdirc", "");
1287 if (!requiredVersion.isEmpty()) {
1288 final String[] bits = requiredVersion.split("-");
1289 return bits[0];
1290 }
1291
1292 return "";
1293 }
1294
1295 /**
1296 * Get the (optional) maximum dmdirc version on which this plugin can run
1297 *
1298 * @return optional maximum dmdirc version on which this plugin can run
1299 */
1300 public String getMaxVersion() {
/*
P/P * Method: String getMaxVersion()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* return_value == &""
*
* Test Vectors:
* java.lang.String:isEmpty(...)@1302: {1}, {0}
*/
1301 final String requiredVersion = getKeyValue("requires", "dmdirc", "");
1302 if (!requiredVersion.isEmpty()) {
1303 final String[] bits = requiredVersion.split("-");
1304 if (bits.length > 1) {
1305 return bits[1];
1306 }
1307 }
1308
1309 return "";
1310 }
1311
1312 /**
1313 * Get the name of the plugin. (Used to identify the plugin)
1314 *
1315 * @return Name of plugin
1316 */
/*
P/P * Method: String getName()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1317 public String getName() { return getKeyValue("metadata", "name", ""); }
1318
1319 /**
1320 * Get the nice name of the plugin. (Displayed to users)
1321 *
1322 * @return Nice Name of plugin
1323 */
/*
P/P * Method: String getNiceName()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1324 public String getNiceName() { return getKeyValue("metadata", "nicename", getName()); }
1325
1326 /**
1327 * String Representation of this plugin
1328 *
1329 * @return String Representation of this plugin
1330 */
1331 @Override
/*
P/P * Method: String toString()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* return_value == &java.lang.StringBuilder:toString(...)
*/
1332 public String toString() { return getNiceName()+" - "+filename; }
1333
1334 /**
1335 * Does this plugin want all its classes loaded?
1336 *
1337 * @return true/false if loadall=true || loadall=yes
1338 */
1339 public boolean loadAll() {
/*
P/P * Method: bool loadAll()
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1340 final String loadAll = getKeyValue("metadata", "loadall", "no");
1341 return loadAll.equalsIgnoreCase("true") || loadAll.equalsIgnoreCase("yes");
1342 }
1343
1344 /**
1345 * Get misc meta-information.
1346 *
1347 * @param metainfo The metainfo to return
1348 * @deprecated Use {@link #getKeyValue(String, String, String) instead
1349 * @return Misc Meta Info (or "" if not found);
1350 */
1351 @Deprecated
/*
P/P * Method: String getMetaInfo(String)
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1352 public String getMetaInfo(final String metainfo) { return getMetaInfo(metainfo,""); }
1353
1354 /**
1355 * Get misc meta-information.
1356 *
1357 * @param metainfo The metainfo to return
1358 * @param fallback Fallback value if requested value is not found
1359 * @deprecated Use {@link #getKeyValue(String, String, String) instead
1360 * @return Misc Meta Info (or fallback if not found);
1361 */
1362 @Deprecated
/*
P/P * Method: String getMetaInfo(String, String)
*
* Preconditions:
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1363 public String getMetaInfo(final String metainfo, final String fallback) { return getKeyValue("misc", metainfo, fallback); }
1364
1365 /**
1366 * Get misc meta-information.
1367 *
1368 * @param metainfo The metainfos to look for in order. If the first item in
1369 * the array is not found, the next will be looked for, and
1370 * so on until either one is found, or none are found.
1371 * @deprecated Use {@link #getKeyValue(String, String, String) instead
1372 * @return Misc Meta Info (or "" if none are found);
1373 */
1374 @Deprecated
/*
P/P * Method: String getMetaInfo(String[])
*
* Preconditions:
* metainfo != null
* metainfo.length <= 232-1
* (soft) init'ed(metainfo[...])
* (soft) init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1375 public String getMetaInfo(final String[] metainfo) { return getMetaInfo(metainfo,""); }
1376
1377 /**
1378 * Get misc meta-information.
1379 *
1380 * @param metainfo The metainfos to look for in order. If the first item in
1381 * the array is not found, the next will be looked for, and
1382 * so on until either one is found, or none are found.
1383 * @param fallback Fallback value if requested values are not found
1384 * @deprecated Use {@link #getKeyValue(String, String, String) instead
1385 * @return Misc Meta Info (or "" if none are found);
1386 */
1387 @Deprecated
1388 public String getMetaInfo(final String[] metainfo, final String fallback) {
/*
P/P * Method: String getMetaInfo(String[], String)
*
* Preconditions:
* metainfo != null
* metainfo.length <= 232-1
* (soft) init'ed(metainfo[...])
* (soft) init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1389 for (String meta : metainfo) {
1390 final String result = getKeyValue("misc", meta, null);
1391 if (result != null) { return result; }
1392 }
1393 return fallback;
1394 }
1395
1396
1397 /**
1398 * Compares this object with the specified object for order.
1399 * Returns a negative integer, zero, or a positive integer as per String.compareTo();
1400 *
1401 * @param o Object to compare to
1402 * @return a negative integer, zero, or a positive integer.
1403 */
1404 @Override
1405 public int compareTo(final PluginInfo o) {
/*
P/P * Method: int compareTo(PluginInfo)
*
* Preconditions:
* o != null
* init'ed(o.metaData)
* init'ed(this.metaData)
*
* Postconditions:
* init'ed(return_value)
*/
1406 return toString().compareTo(o.toString());
1407 }
1408
1409 /**
1410 * Update exports list.
1411 */
1412 private void updateExports() {
/*
P/P * Method: void updateExports()
*
* Preconditions:
* this.exports != null
* this.metaData != null
*
* Presumptions:
* java.util.Iterator:next(...)@1418 != null
*
* Postconditions:
* possibly_updated(com/dmdirc/plugins/PluginManager.me)
* java.lang.StringBuilder:toString(...)._tainted == 0
* new HashMap(PluginManager#2) num objects == 0
* new Hashtable(PluginManager#1) num objects == 0
* new PluginClassLoader(getSubClassLoader#1) num objects == 0
* possibly_updated(new PluginClassLoader(getSubClassLoader#1).pluginInfo)
* new PluginManager(getPluginManager#1) num objects == 0
* init'ed(new PluginManager(getPluginManager#1).knownPlugins)
* init'ed(new PluginManager(getPluginManager#1).myDir)
* init'ed(new PluginManager(getPluginManager#1).services)
*
* Test Vectors:
* com.dmdirc.util.ConfigFile:getFlatDomain(...)@1416: Addr_Set{null}, Inverse{null}
* java.util.Iterator:hasNext(...)@1418: {0}, {1}
*/
1413 exports.clear();
1414
1415 // Get exports provided by this plugin
1416 final List<String> exportsList = metaData.getFlatDomain("exports");
1417 if (exportsList != null) {
1418 for (String item : exportsList) {
1419 final String[] bits = item.split(" ");
1420 if (bits.length > 2) {
1421 final String methodName = bits[0];
1422 final String methodClass = bits[2];
1423 final String serviceName = (bits.length > 4) ? bits[4] : bits[0];
1424
1425 // Add a provides for this
1426 final Service service = PluginManager.getPluginManager().getService("export", serviceName, true);
1427 service.addProvider(this);
1428 provides.add(service);
1429 // Add is as an export
1430 exports.put(serviceName, new ExportInfo(methodName, methodClass, this));
1431 }
1432 }
1433 }
1434 }
1435
1436 /**
1437 * Get an ExportedService object from this provider.
1438 *
1439 * @param name Service name
1440 * @return ExportedService object. If no such service exists, the execute
1441 * method of this ExportedService will always return null.
1442 */
1443 @Override
1444 public ExportedService getExportedService(final String name) {
/*
P/P * Method: ExportedService getExportedService(String)
*
* Preconditions:
* this.exports != null
* (soft) com/dmdirc/plugins/GlobalClassLoader.me != null
*
* Presumptions:
* com/dmdirc/plugins/GlobalClassLoader.me.resourcesList@1446 != null
* java.util.Map:get(...).className@1446 != null
* java.util.Map:get(...).pluginInfo...url@1446 != null
* java.util.Map:get(...).pluginInfo.classloader.pluginInfo@1446 != null
* java.util.Map:get(...).pluginInfo.classloader@1446 != null
* ...
*
* Postconditions:
* init'ed(com/dmdirc/plugins/GlobalClassLoader.me)
* return_value in Addr_Set{&new ExportedService(getExportedService#1),&new ExportedService(getExportedService#1*),&new ExportedService(getExportedService#2*)}
* new ExportedService(getExportedService#1) num objects <= 1
* init'ed(new ExportedService(getExportedService#1).myMethod)
* new ExportedService(getExportedService#1).myObject == null
* new ExportedService(getExportedService#1*) num objects <= 1
* init'ed(new ExportedService(getExportedService#1*).myMethod)
* init'ed(new ExportedService(getExportedService#1*).myObject)
* new ExportedService(getExportedService#2*) num objects <= 1
* init'ed(new ExportedService(getExportedService#2*).myMethod)
* ...
*
* Test Vectors:
* java.util.Map:containsKey(...)@1445: {0}, {1}
*/
1445 if (exports.containsKey(name)) {
1446 return exports.get(name).getExportedService();
1447 } else {
1448 return new ExportedService(null, null);
1449 }
1450 }
1451
1452 /**
1453 * Get the Plugin object for this plugin.
1454 *
1455 * @return Plugin object for the plugin
1456 */
1457 protected Plugin getPluginObject() {
/*
P/P * Method: Plugin getPluginObject()
*
* Preconditions:
* init'ed(this.plugin)
*
* Postconditions:
* return_value == this.plugin
* init'ed(return_value)
*/
1458 return plugin;
1459 }
1460 }
SofCheck Inspector Build Version : 2.17854
| PluginInfo.java |
2009-Jun-25 01:54:24 |
| PluginInfo.class |
2009-Sep-02 17:04:13 |
| PluginInfo$1.class |
2009-Sep-02 17:04:13 |