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

Last change on this file since 2858 was 2858, checked in by mjulius, 14 years ago

message fixes

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