source: josm/trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java@ 2826

Last change on this file since 2826 was 2817, checked in by Gubaer, 14 years ago

fixed #3063: Downloading a plugin yields 3 dialogs at the same time: Downloading plugin / You should restart JOSM / Plugin downloaded
fixed #3628: JOSM blocking itself updating broken plugin
fixed #4187: JOSM deleted random files from disk after start (data loss)
fixed #4199: new version - plugins update vs josm start [should be fixed. Be careful if you have two JOSM instances running. Auto-update of plugins in the second instance will fail because plugin files are locked by the first instance]
fixed #4034: JOSM should auto-download plugin list when it hasn't been downloaded before [JOSM now displays a hint]

fixed: splash screen showing again even if plugins are auto-updated
new: progress indication integrated in splash screen
new: cancelable, asynchronous download of plugins from preferences
new: cancelable, asynchronous download of plugin list from plugin download sites
new: asynchronous loading of plugin information, launch of preferences dialog accelerated
refactored: clean up, documentation of plugin management code (PluginHandler)

File size: 31.3 KB
Line 
1//License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.plugins;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.Font;
9import java.awt.GridBagLayout;
10import java.awt.event.ActionEvent;
11import java.io.File;
12import java.io.FilenameFilter;
13import java.net.URL;
14import java.net.URLClassLoader;
15import java.util.ArrayList;
16import java.util.Arrays;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.Comparator;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27import java.util.Map.Entry;
28import java.util.concurrent.ExecutionException;
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.Executors;
31import java.util.concurrent.Future;
32
33import javax.swing.AbstractAction;
34import javax.swing.BorderFactory;
35import javax.swing.Box;
36import javax.swing.JButton;
37import javax.swing.JLabel;
38import javax.swing.JOptionPane;
39import javax.swing.JPanel;
40import javax.swing.JScrollPane;
41import javax.swing.JTextArea;
42import javax.swing.UIManager;
43
44import org.openstreetmap.josm.Main;
45import org.openstreetmap.josm.data.Version;
46import org.openstreetmap.josm.gui.ExtendedDialog;
47import org.openstreetmap.josm.gui.MapFrame;
48import org.openstreetmap.josm.gui.download.DownloadSelection;
49import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
50import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
51import org.openstreetmap.josm.gui.progress.ProgressMonitor;
52import org.openstreetmap.josm.tools.CheckParameterUtil;
53import org.openstreetmap.josm.tools.GBC;
54import org.openstreetmap.josm.tools.ImageProvider;
55
56/**
57 * PluginHandler is basically a collection of static utility functions used to bootstrap
58 * and manage the loaded plugins.
59 *
60 */
61public class PluginHandler {
62
63 final public static String [] DEPRECATED_PLUGINS = new String[] {"mappaint", "unglueplugin",
64 "lang-de", "lang-en_GB", "lang-fr", "lang-it", "lang-pl", "lang-ro",
65 "lang-ru", "ewmsplugin", "ywms", "tways-0.2", "geotagged", "landsat",
66 "namefinder", "waypoints", "slippy_map_chooser", "tcx-support", "usertools",
67 "AgPifoJ", "utilsplugin"};
68
69 final public static String [] UNMAINTAINED_PLUGINS = new String[] {"gpsbabelgui", "Intersect_way"};
70
71 /**
72 * All installed and loaded plugins (resp. their main classes)
73 */
74 public final static Collection<PluginProxy> pluginList = new LinkedList<PluginProxy>();
75
76
77 /**
78 * Removes deprecated plugins from a collection of plugins. Modifies the
79 * collection <code>plugins</code>.
80 *
81 * Also notifies the user about removed deprecated plugins
82 *
83 * @param plugins the collection of plugins
84 */
85 private static void filterDeprecatedPlugins(Collection<String> plugins) {
86 Set<String> removedPlugins = new HashSet<String>();
87 for (String p : DEPRECATED_PLUGINS) {
88 if (plugins.contains(p)) {
89 plugins.remove(p);
90 Main.pref.removeFromCollection("plugins", p);
91 removedPlugins.add(p);
92 }
93 }
94 if (removedPlugins.isEmpty())
95 return;
96
97 // notify user about removed deprecated plugins
98 //
99 StringBuffer sb = new StringBuffer();
100 sb.append("<html>");
101 sb.append(trn(
102 "The following plugin is no longer necessary and has been deactivated:",
103 "The following plugins are no longer necessary and have been deactivated:",
104 removedPlugins.size()
105 ));
106 sb.append("<ul>");
107 for (String name: removedPlugins) {
108 sb.append("<li>").append(name).append("</li>");
109 }
110 sb.append("</ul>");
111 sb.append("</html>");
112 JOptionPane.showMessageDialog(
113 Main.parent,
114 sb.toString(),
115 tr("Warning"),
116 JOptionPane.WARNING_MESSAGE
117 );
118 }
119
120 /**
121 * Removes unmaintained plugins from a collection of plugins. Modifies the
122 * collection <code>plugins</code>. Also removes the plugin from the list
123 * of plugins in the preferences, if necessary.
124 *
125 * Asks the user for every unmaintained plugin whether it should be removed.
126 *
127 * @param plugins the collection of plugins
128 */
129 private static void filterUnmaintainedPlugins(Collection<String> plugins) {
130 for (String unmaintained : UNMAINTAINED_PLUGINS) {
131 if (!plugins.contains(unmaintained)) {
132 continue;
133 }
134 String msg = tr("<html>Loading of {0} plugin was requested."
135 + "<br>This plugin is no longer developed and very likely will produce errors."
136 +"<br>It should be disabled.<br>Delete from preferences?</html>", unmaintained);
137 if (confirmDisablePlugin(msg,unmaintained)) {
138 Main.pref.removeFromCollection("plugins", unmaintained);
139 plugins.remove(unmaintained);
140 }
141 }
142 }
143
144 /**
145 * Checks whether the locally available plugins should be updated and
146 * asks the user if running an update is OK. An update is advised if
147 * JOSM was updated to a new version since the last plugin updates or
148 * if the plugins were last updated a long time ago.
149 *
150 * @return true if a plugin update should be run; false, otherwise
151 */
152 public static boolean checkAndConfirmPluginUpdate() {
153 String message = null;
154 String togglePreferenceKey = null;
155 int v = Version.getInstance().getVersion();
156 if (Main.pref.getInteger("pluginmanager.version", 0) < v) {
157 message = tr("<html>You updated your JOSM software.<br>"
158 + "To prevent problems the plugins should be updated as well.<br><br>"
159 + "Update plugins now?"
160 + "</html>"
161 );
162 togglePreferenceKey = "pluginmanger.version";
163 } else {
164 long tim = System.currentTimeMillis();
165 long last = Main.pref.getLong("pluginmanager.lastupdate", 0);
166 Integer maxTime = Main.pref.getInteger("pluginmanager.warntime", 60);
167 long d = (tim - last) / (24 * 60 * 60 * 1000l);
168 if ((last <= 0) || (maxTime <= 0)) {
169 Main.pref.put("pluginmanager.lastupdate", Long.toString(tim));
170 } else if (d > maxTime) {
171 message = tr("Last plugin update more than {0} days ago.", d);
172 togglePreferenceKey = "pluginmanager.time";
173 }
174 }
175 if (message == null) return false;
176
177 // ask whether update is fine
178 //
179 ExtendedDialog dialog = new ExtendedDialog(
180 Main.parent,
181 tr("Update plugins"),
182 new String[] {
183 tr("Update plugins"), tr("Skip update")
184 }
185 );
186 dialog.setContent(message);
187 dialog.toggleEnable(togglePreferenceKey);
188 dialog.setButtonIcons( new String[] {"dialogs/refresh.png", "cancel.png"});
189 dialog.configureContextsensitiveHelp(ht("/Plugin/AutomaticUpdate"), true /* show help button */);
190 dialog.showDialog();
191 return dialog.getValue() == 1;
192 }
193
194 /**
195 * Alerts the user if a plugin required by another plugin is missing
196 *
197 * @param plugin the the plugin
198 * @param missingRequiredPlugin the missing required plugin
199 */
200 private static void alertMissingRequiredPlugin(String plugin, Set<String> missingRequiredPlugin) {
201 StringBuilder sb = new StringBuilder();
202 sb.append("<html>");
203 sb.append(trn("A required plugin for plugin {0} was not found. The required plugin is:",
204 "{1} required plugins for plugin {0} were not found. The required plugins are:",
205 missingRequiredPlugin.size(),
206 plugin,
207 missingRequiredPlugin.size()
208 ));
209 sb.append("<ul>");
210 for (String p: missingRequiredPlugin) {
211 sb.append("<li>").append(p).append("</li>");
212 }
213 sb.append("</ul>").append("</html>");
214 JOptionPane.showMessageDialog(
215 Main.parent,
216 sb.toString(),
217 tr("Error"),
218 JOptionPane.ERROR_MESSAGE
219 );
220 }
221
222 /**
223 * Checks whether all preconditions for loading the plugin <code>plugin</code> are met. The
224 * current JOSM version must be compatible with the plugin and no other plugins this plugin
225 * depends on should be missing.
226 *
227 * @param plugins the collection of all loaded plugins
228 * @param plugin the plugin for which preconditions are checked
229 * @return true, if the preconditions are met; false otherwise
230 */
231 public static boolean checkLoadPreconditions(Collection<PluginInformation> plugins, PluginInformation plugin) {
232
233 // make sure the plugin is compatible with the current JOSM version
234 //
235 int josmVersion = Version.getInstance().getVersion();
236 if (plugin.mainversion > josmVersion && josmVersion != Version.JOSM_UNKNOWN_VERSION) {
237 JOptionPane.showMessageDialog(
238 Main.parent,
239 tr("Plugin {0} requires JOSM update to version {1}.", plugin.name,
240 plugin.mainversion),
241 tr("Warning"),
242 JOptionPane.WARNING_MESSAGE
243 );
244 return false;
245 }
246
247 // make sure the dependencies to other plugins are not broken
248 //
249 if(plugin.requires != null){
250 Set<String> pluginNames = new HashSet<String>();
251 for (PluginInformation pi: plugins) {
252 pluginNames.add(pi.name);
253 }
254 Set<String> missingPlugins = new HashSet<String>();
255 for (String requiredPlugin : plugin.requires.split(";")) {
256 if (!pluginNames.contains(requiredPlugin)) {
257 missingPlugins.add(requiredPlugin);
258 }
259 }
260 if (!missingPlugins.isEmpty()) {
261 alertMissingRequiredPlugin(plugin.name, missingPlugins);
262 return false;
263 }
264 }
265 return true;
266 }
267
268 /**
269 * Creates a class loader for loading plugin code.
270 *
271 * @param plugins the collection of plugins which are going to be loaded with this
272 * class loader
273 * @return the class loader
274 */
275 public static ClassLoader createClassLoader(Collection<PluginInformation> plugins) {
276 // iterate all plugins and collect all libraries of all plugins:
277 List<URL> allPluginLibraries = new LinkedList<URL>();
278 File pluginDir = Main.pref.getPluginsDirectory();
279 for (PluginInformation info : plugins) {
280 if (info.libraries == null) {
281 continue;
282 }
283 allPluginLibraries.addAll(info.libraries);
284 File pluginJar = new File(pluginDir, info.name + ".jar");
285 URL pluginJarUrl = PluginInformation.fileToURL(pluginJar);
286 allPluginLibraries.add(pluginJarUrl);
287 }
288
289 // create a classloader for all plugins:
290 URL[] jarUrls = new URL[allPluginLibraries.size()];
291 jarUrls = allPluginLibraries.toArray(jarUrls);
292 URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader());
293 return pluginClassLoader;
294 }
295
296 /**
297 * Loads and instantiates the plugin described by <code>plugin</code> using
298 * the class loader <code>pluginClassLoader</code>.
299 *
300 * @param plugin the plugin
301 * @param pluginClassLoader the plugin class loader
302 */
303 public static void loadPlugin(PluginInformation plugin, ClassLoader pluginClassLoader) {
304 try {
305 Class<?> klass = plugin.loadClass(pluginClassLoader);
306 if (klass != null) {
307 System.out.println(tr("loading plugin ''{0}''", plugin.name));
308 pluginList.add(plugin.load(klass));
309 }
310 } catch (Throwable e) {
311 e.printStackTrace();
312 String msg = tr("Could not load plugin {0}. Delete from preferences?", plugin.name);
313 if (confirmDisablePlugin(msg, plugin.name)) {
314 Main.pref.removeFromCollection("plugins", plugin.name);
315 }
316 }
317 }
318
319 /**
320 * Loads the plugin in <code>plugins</code> from locally available jar files into
321 * memory.
322 *
323 * @param plugins the list of plugins
324 * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
325 */
326 public static void loadPlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) {
327 if (monitor == null) {
328 monitor = NullProgressMonitor.INSTANCE;
329 }
330 try {
331 monitor.beginTask(tr("Loading plugins ..."));
332 List<PluginInformation> toLoad = new LinkedList<PluginInformation>();
333 // sort the plugins according to their "staging" equivalence class. The
334 // lower the value of "stage" the earlier the plugin should be loaded.
335 //
336 Collections.sort(
337 toLoad,
338 new Comparator<PluginInformation>() {
339 public int compare(PluginInformation o1, PluginInformation o2) {
340 if (o1.stage < o2.stage) return -1;
341 if (o1.stage == o2.stage) return 0;
342 return 1;
343 }
344 }
345 );
346 monitor.subTask(tr("Checking plugin preconditions..."));
347 for (PluginInformation pi: plugins) {
348 if (checkLoadPreconditions(plugins, pi)) {
349 toLoad.add(pi);
350 }
351 }
352 if (toLoad.isEmpty())
353 return;
354
355 ClassLoader pluginClassLoader = createClassLoader(toLoad);
356 ImageProvider.sources.add(0, pluginClassLoader);
357 monitor.setTicksCount(toLoad.size());
358 for (PluginInformation info : toLoad) {
359 monitor.setExtraText(tr("Loading plugin ''{0}''...", info.name));
360 loadPlugin(info, pluginClassLoader);
361 monitor.worked(1);
362 }
363 } finally {
364 monitor.finishTask();
365 }
366 }
367
368 /**
369 * Loads plugins from <code>plugins</code> which have the flag {@see PluginInformation#early}
370 * set to true.
371 *
372 * @param plugins the collection of plugins
373 * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
374 */
375 public static void loadEarlyPlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) {
376 List<PluginInformation> earlyPlugins = new ArrayList<PluginInformation>(plugins.size());
377 for (PluginInformation pi: plugins) {
378 if (pi.early) {
379 earlyPlugins.add(pi);
380 }
381 }
382 loadPlugins(earlyPlugins, monitor);
383 }
384
385 /**
386 * Loads plugins from <code>plugins</code> which have the flag {@see PluginInformation#early}
387 * set to false.
388 *
389 * @param plugins the collection of plugins
390 * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
391 */
392 public static void loadLatePlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) {
393 List<PluginInformation> latePlugins = new ArrayList<PluginInformation>(plugins.size());
394 for (PluginInformation pi: plugins) {
395 if (!pi.early) {
396 latePlugins.add(pi);
397 }
398 }
399 loadPlugins(latePlugins, monitor);
400 }
401
402 /**
403 * Loads locally available plugin information from local plugin jars and from cached
404 * plugin lists.
405 *
406 * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
407 * @return the list of locally available plugin information
408 *
409 */
410 private static Map<String, PluginInformation> loadLocallyAvailablePluginInformation(ProgressMonitor monitor) {
411 if (monitor == null) {
412 monitor = NullProgressMonitor.INSTANCE;
413 }
414 try {
415 ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask(monitor);
416 ExecutorService service = Executors.newSingleThreadExecutor();
417 Future<?> future = service.submit(task);
418 try {
419 future.get();
420 } catch(ExecutionException e) {
421 e.printStackTrace();
422 return null;
423 } catch(InterruptedException e) {
424 e.printStackTrace();
425 return null;
426 }
427 HashMap<String, PluginInformation> ret = new HashMap<String, PluginInformation>();
428 for (PluginInformation pi: task.getAvailablePlugins()) {
429 ret.put(pi.name, pi);
430 }
431 return ret;
432 } finally {
433 monitor.finishTask();
434 }
435 }
436
437 private static void alertMissingPluginInformation(Collection<String> plugins) {
438 StringBuilder sb = new StringBuilder();
439 sb.append("<html>");
440 sb.append(trn("JOSM could not find information about the following plugin:",
441 "JOSM could not find information about the following plugins:",
442 plugins.size()));
443 sb.append("<ul>");
444 for (String plugin: plugins) {
445 sb.append("<li>").append(plugin).append("</li>");
446 }
447 sb.append("</ul>");
448 sb.append(trn("The plugin is not going to be loaded.",
449 "The plugins are not going to be loaded.",
450 plugins.size()));
451 sb.append("</html>");
452 JOptionPane.showMessageDialog(
453 Main.parent,
454 sb.toString(),
455 tr("Warning"),
456 JOptionPane.WARNING_MESSAGE
457 );
458 }
459
460 /**
461 * Builds the set of plugins to load. Deprecated and unmaintained plugins are filtered
462 * out. This involves user interaction. This method displays alert and confirmation
463 * messages.
464 *
465 * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
466 * @return the set of plugins to load (as set of plugin names)
467 */
468 public static List<PluginInformation> buildListOfPluginsToLoad(ProgressMonitor monitor) {
469 if (monitor == null) {
470 monitor = NullProgressMonitor.INSTANCE;
471 }
472 try {
473 monitor.beginTask(tr("Determine plugins to load..."));
474 Set<String> plugins = new HashSet<String>();
475 plugins.addAll(Main.pref.getCollection("plugins", new LinkedList<String>()));
476 if (System.getProperty("josm.plugins") != null) {
477 plugins.addAll(Arrays.asList(System.getProperty("josm.plugins").split(",")));
478 }
479 monitor.subTask(tr("Removing deprecated plugins..."));
480 filterDeprecatedPlugins(plugins);
481 monitor.subTask(tr("Removing umaintained plugins..."));
482 filterUnmaintainedPlugins(plugins);
483 Map<String, PluginInformation> infos = loadLocallyAvailablePluginInformation(monitor.createSubTaskMonitor(1,false));
484 List<PluginInformation> ret = new LinkedList<PluginInformation>();
485 for (Iterator<String> it = plugins.iterator(); it.hasNext();) {
486 String plugin = it.next();
487 if (infos.containsKey(plugin)) {
488 ret.add(infos.get(plugin));
489 it.remove();
490 }
491 }
492 if (!plugins.isEmpty()) {
493 alertMissingPluginInformation(plugins);
494 }
495 return ret;
496 } finally {
497 monitor.finishTask();
498 }
499 }
500
501 private static void alertFailedPluginUpdate(Collection<PluginInformation> plugins) {
502 StringBuffer sb = new StringBuffer();
503 sb.append("<html>");
504 sb.append(trn(
505 "Updating the following plugin has failed:",
506 "Updating the following plugins has failed:",
507 plugins.size()
508 )
509 );
510 sb.append("<ul>");
511 for (PluginInformation pi: plugins) {
512 sb.append("<li>").append(pi.name).append("</li>");
513 }
514 sb.append("</ul>");
515 sb.append(tr("Please open the Preference Dialog after JOSM has started and try to update them manually."));
516 sb.append("</html>");
517 JOptionPane.showMessageDialog(
518 Main.parent,
519 sb.toString(),
520 tr("Plugin update failed"),
521 JOptionPane.ERROR_MESSAGE
522 );
523 }
524
525 /**
526 * Updates the plugins in <code>plugins</code>.
527 *
528 * @param plugins the collection of plugins to update. Must not be null.
529 * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
530 * @throws IllegalArgumentException thrown if plugins is null
531 */
532 public static void updatePlugins(Collection<PluginInformation> plugins, ProgressMonitor monitor) throws IllegalArgumentException{
533 CheckParameterUtil.ensureParameterNotNull(plugins, "plugins");
534 if (monitor == null) {
535 monitor = NullProgressMonitor.INSTANCE;
536 }
537 try {
538 PluginDownloadTask task = new PluginDownloadTask(
539 monitor,
540 plugins,
541 tr("Update plugins")
542 );
543 ExecutorService service = Executors.newSingleThreadExecutor();
544 Future<?> future = service.submit(task);
545 try {
546 future.get();
547 } catch(ExecutionException e) {
548 e.printStackTrace();
549 } catch(InterruptedException e) {
550 e.printStackTrace();
551 }
552 if (! task.getFailedPlugins().isEmpty()) {
553 alertFailedPluginUpdate(task.getFailedPlugins());
554 return;
555 }
556 } finally {
557 monitor.finishTask();
558 }
559 // remember the update because it was successful
560 //
561 Main.pref.putInteger("pluginmanager.version", Version.getInstance().getVersion());
562 Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis()));
563 }
564
565 /**
566 * Ask the user for confirmation that a plugin shall be disabled.
567 *
568 * @param reason the reason for disabling the plugin
569 * @param name the plugin name
570 * @return true, if the plugin shall be disabled; false, otherwise
571 */
572 public static boolean confirmDisablePlugin(String reason, String name) {
573 ExtendedDialog dialog = new ExtendedDialog(
574 Main.parent,
575 tr("Disable plugin"),
576 new String[] {
577 tr("Disable plugin"), tr("Keep plugin")
578 }
579 );
580 dialog.setContent(reason);
581 dialog.setButtonIcons(new String[] { "dialogs/delete.png", "cancel.png" });
582 dialog.showDialog();
583 return dialog.getValue() == 1;
584 }
585
586 /**
587 * Notified loaded plugins about a new map frame
588 *
589 * @param old the old map frame
590 * @param map the new map frame
591 */
592 public static void notifyMapFrameChanged(MapFrame old, MapFrame map) {
593 for (PluginProxy plugin : pluginList) {
594 plugin.mapFrameInitialized(old, map);
595 }
596 }
597
598 public static Object getPlugin(String name) {
599 for (PluginProxy plugin : pluginList)
600 if(plugin.info.name.equals(name))
601 return plugin.plugin;
602 return null;
603 }
604
605 public static void addDownloadSelection(List<DownloadSelection> downloadSelections) {
606 for (PluginProxy p : pluginList) {
607 p.addDownloadSelection(downloadSelections);
608 }
609 }
610
611 public static void getPreferenceSetting(Collection<PreferenceSettingFactory> settings) {
612 for (PluginProxy plugin : pluginList) {
613 settings.add(new PluginPreferenceFactory(plugin));
614 }
615 }
616
617 /**
618 * Installs downloaded plugins. Moves files with the suffix ".jar.new" to the corresponding
619 * ".jar" files.
620 *
621 */
622 public static void installDownloadedPlugins() {
623 File pluginDir = Main.pref.getPluginsDirectory();
624 if (! pluginDir.exists() || ! pluginDir.isDirectory() || ! pluginDir.canWrite())
625 return;
626
627 final File[] files = pluginDir.listFiles(new FilenameFilter() {
628 public boolean accept(File dir, String name) {
629 return name.endsWith(".jar.new");
630 }});
631
632 for (File updatedPlugin : files) {
633 final String filePath = updatedPlugin.getPath();
634 File plugin = new File(filePath.substring(0, filePath.length() - 4));
635 String pluginName = updatedPlugin.getName().substring(0, updatedPlugin.getName().length() - 8);
636 if (plugin.exists()) {
637 if (!plugin.delete()) {
638 System.err.println(tr("Warning: failed to delete outdated plugin ''{0}''.", plugin.toString()));
639 System.err.println(tr("Warning: failed to install already downloaded plugin ''{0}''. Skipping installation. JOSM still going to load the old plugin version.", pluginName));
640 continue;
641 }
642 }
643 if (!updatedPlugin.renameTo(plugin)) {
644 System.err.println(tr("Warning: failed to install plugin ''{0}'' from temporary download file ''{1}''. Renaming failed.", plugin.toString(), updatedPlugin.toString()));
645 System.err.println(tr("Warning: failed to install already downloaded plugin ''{0}''. Skipping installation. JOSM still going to load the old plugin version.", pluginName));
646 }
647 }
648 return;
649 }
650
651 public static boolean checkException(Throwable e)
652 {
653 PluginProxy plugin = null;
654
655 // Check for an explicit problem when calling a plugin function
656 if (e instanceof PluginException) {
657 plugin = ((PluginException)e).plugin;
658 }
659
660 if (plugin == null)
661 {
662 /**
663 * Analyze the stack of the argument and find a name of a plugin, if
664 * some known problem pattern has been found.
665 */
666 for (PluginProxy p : pluginList)
667 {
668 String baseClass = p.info.className;
669 int i = baseClass.lastIndexOf(".");
670 baseClass = baseClass.substring(0, i);
671 for (StackTraceElement element : e.getStackTrace())
672 {
673 String c = element.getClassName();
674 if(c.startsWith(baseClass))
675 {
676 plugin = p;
677 break;
678 }
679 }
680 if(plugin != null) {
681 break;
682 }
683 }
684 }
685
686 if (plugin != null) {
687 ExtendedDialog dialog = new ExtendedDialog(
688 Main.parent,
689 tr("Disable plugin"),
690 new String[] {tr("Disable plugin"), tr("Cancel")}
691 );
692 dialog.setButtonIcons(new String[] {"dialogs/delete.png", "cancel.png"});
693 dialog.setContent(
694 tr("<html>") +
695 tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.", plugin.info.name)
696 + "<br>"
697 + (plugin.info.author != null
698 ? tr("According to the information within the plugin, the author is {0}.", plugin.info.author)
699 : "")
700 + "<br>"
701 + tr("Try updating to the newest version of this plugin before reporting a bug.")
702 + "<br>"
703 + tr("Should the plugin be disabled?")
704 + "</html>"
705 );
706 dialog.showDialog();
707 int answer = dialog.getValue();
708
709 if (answer == 1) {
710 List<String> plugins = new ArrayList<String>(Main.pref.getCollection("plugins", Collections.<String>emptyList()));
711 if (plugins.contains(plugin.info.name)) {
712 while (plugins.remove(plugin.info.name)) {}
713 Main.pref.putCollection("plugins", plugins);
714 JOptionPane.showMessageDialog(Main.parent,
715 tr("The plugin has been removed from the configuration. Please restart JOSM to unload the plugin."),
716 tr("Information"),
717 JOptionPane.INFORMATION_MESSAGE);
718 } else {
719 JOptionPane.showMessageDialog(
720 Main.parent,
721 tr("The plugin could not be removed. Probably it was already disabled"),
722 tr("Error"),
723 JOptionPane.ERROR_MESSAGE
724 );
725 }
726 return true;
727 }
728 }
729 return false;
730 }
731
732 public static String getBugReportText() {
733 String text = "";
734 String pl = Main.pref.getCollectionAsString("plugins");
735 if (pl != null && pl.length() != 0) {
736 text += "Plugins: " + pl + "\n";
737 }
738 for (final PluginProxy pp : pluginList) {
739 text += "Plugin "
740 + pp.info.name
741 + (pp.info.version != null && !pp.info.version.equals("") ? " Version: " + pp.info.version + "\n"
742 : "\n");
743 }
744 return text;
745 }
746
747 public static JPanel getInfoPanel() {
748 JPanel pluginTab = new JPanel(new GridBagLayout());
749 for (final PluginProxy p : pluginList) {
750 String name = p.info.name
751 + (p.info.version != null && !p.info.version.equals("") ? " Version: " + p.info.version : "");
752 pluginTab.add(new JLabel(name), GBC.std());
753 pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
754 pluginTab.add(new JButton(new AbstractAction(tr("Information")) {
755 public void actionPerformed(ActionEvent event) {
756 StringBuilder b = new StringBuilder();
757 for (Entry<String, String> e : p.info.attr.entrySet()) {
758 b.append(e.getKey());
759 b.append(": ");
760 b.append(e.getValue());
761 b.append("\n");
762 }
763 JTextArea a = new JTextArea(10, 40);
764 a.setEditable(false);
765 a.setText(b.toString());
766 JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a), tr("Plugin information"),
767 JOptionPane.INFORMATION_MESSAGE);
768 }
769 }), GBC.eol());
770
771 JTextArea description = new JTextArea((p.info.description == null ? tr("no description available")
772 : p.info.description));
773 description.setEditable(false);
774 description.setFont(new JLabel().getFont().deriveFont(Font.ITALIC));
775 description.setLineWrap(true);
776 description.setWrapStyleWord(true);
777 description.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0));
778 description.setBackground(UIManager.getColor("Panel.background"));
779
780 pluginTab.add(description, GBC.eop().fill(GBC.HORIZONTAL));
781 }
782 return pluginTab;
783 }
784}
Note: See TracBrowser for help on using the repository browser.