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

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

fixed an ugly hack in the plugin bootstrap procedure

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