Changeset 2817 in josm


Ignore:
Timestamp:
2010-01-11T21:06:49+01:00 (11 years ago)
Author:
Gubaer
Message:

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)

Location:
trunk/src/org/openstreetmap/josm
Files:
9 added
2 deleted
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/Main.java

    r2801 r2817  
    4747import org.openstreetmap.josm.gui.MainMenu;
    4848import org.openstreetmap.josm.gui.MapFrame;
    49 import org.openstreetmap.josm.gui.SplashScreen;
    5049import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    5150import org.openstreetmap.josm.gui.io.SaveLayersDialog;
     
    174173        Main.map = map;
    175174
    176         PluginHandler.setMapFrame(old, map);
     175        PluginHandler.notifyMapFrameChanged(old, map);
    177176    }
    178177
     
    192191
    193192    public Main() {
    194         this(null);
    195     }
    196 
    197     public Main(SplashScreen splash) {
    198193        main = this;
    199194        //        platform = determinePlatformHook();
     
    201196        contentPane.add(panel, BorderLayout.CENTER);
    202197        panel.add(gettingStarted, BorderLayout.CENTER);
    203 
    204         if(splash != null) {
    205             splash.setStatus(tr("Creating main GUI"));
    206         }
    207198        menu = new MainMenu();
    208199
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r2711 r2817  
    202202    }
    203203
    204     public File getPluginsDirFile() {
     204    public File getPluginsDirectory() {
    205205        return new File(getPreferencesDirFile(), "plugins");
    206206    }
     
    748748        System.setProperties(sysProp);
    749749    }
     750
     751    /**
     752     * The default plugin site
     753     */
     754    private final static String[] DEFAULT_PLUGIN_SITE = {"http://josm.openstreetmap.de/plugin"};
     755
     756    /**
     757     * Replies the collection of plugin site URLs from where plugin lists can be downloaded
     758     *
     759     * @return
     760     */
     761    public Collection<String> getPluginSites() {
     762        return getCollection("pluginmanager.sites", Arrays.asList(DEFAULT_PLUGIN_SITE));
     763    }
     764
     765    /**
     766     * Sets the collection of plugin site URLs.
     767     *
     768     * @param sites the site URLs
     769     */
     770    public void setPluginSites(Collection<String> sites) {
     771        putCollection("pluginmanager.sites", sites);
     772    }
     773
    750774}
  • trunk/src/org/openstreetmap/josm/gui/MainApplication.java

    r2801 r2817  
    2222import org.openstreetmap.josm.Main;
    2323import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
     24import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    2425import org.openstreetmap.josm.io.DefaultProxySelector;
    2526import org.openstreetmap.josm.io.auth.CredentialsManagerFactory;
    2627import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
    2728import org.openstreetmap.josm.plugins.PluginHandler;
     29import org.openstreetmap.josm.plugins.PluginInformation;
    2830import org.openstreetmap.josm.tools.BugReportExceptionHandler;
    2931import org.openstreetmap.josm.tools.I18n;
     
    4547     * display the frame.
    4648     */
    47     public MainApplication(JFrame mainFrame, SplashScreen splash) {
    48         super(splash);
     49    public MainApplication(JFrame mainFrame) {
     50        super();
    4951        mainFrame.setContentPane(contentPane);
    5052        mainFrame.setJMenuBar(menu);
     
    151153        }
    152154
    153         SplashScreen splash = new SplashScreen(Main.pref.getBoolean("draw.splashscreen", true));
    154 
    155         splash.setStatus(tr("Activating updated plugins"));
    156         PluginHandler.earlyCleanup();
    157 
    158         splash.setStatus(tr("Loading early plugins"));
    159         PluginHandler.loadPlugins(true);
    160 
    161         splash.setStatus(tr("Setting defaults"));
     155        SplashScreen splash = new SplashScreen();
     156        ProgressMonitor monitor = splash.getProgressMonitor();
     157        monitor.beginTask(tr("Initializing"));
     158        monitor.setTicksCount(7);
     159        splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
     160
     161        List<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(monitor.createSubTaskMonitor(1, false));
     162        if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate()) {
     163            monitor.subTask(tr("Updating plugins..."));
     164            PluginHandler.updatePlugins(pluginsToLoad, monitor.createSubTaskMonitor(1, false));
     165        }
     166        monitor.worked(1);
     167
     168        monitor.subTask(tr("Installing updated plugins"));
     169        PluginHandler.installDownloadedPlugins();
     170        monitor.worked(1);
     171
     172        monitor.subTask(tr("Loading early plugins"));
     173        PluginHandler.loadEarlyPlugins(pluginsToLoad, monitor.createSubTaskMonitor(1, false));
     174        monitor.worked(1);
     175
     176        monitor.subTask(tr("Setting defaults"));
    162177        preConstructorInit(args);
    163178        removeObsoletePreferences();
    164         splash.setStatus(tr("Creating main GUI"));
     179        monitor.worked(1);
     180
     181        monitor.indeterminateSubTask(tr("Creating main GUI"));
    165182        JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
    166183        Main.parent = mainFrame;
    167         final Main main = new MainApplication(mainFrame, splash);
    168         splash.setStatus(tr("Loading plugins"));
    169         PluginHandler.loadPlugins(false);
     184        final Main main = new MainApplication(mainFrame);
     185        monitor.worked(1);
     186
     187        monitor.subTask(tr("Loading plugins"));
     188        PluginHandler.loadLatePlugins(pluginsToLoad,  monitor.createSubTaskMonitor(1, false));
     189        monitor.worked(1);
    170190        toolbar.refreshToolbarControl();
    171 
     191        splash.setVisible(false);
     192        splash.dispose();
    172193        mainFrame.setVisible(true);
    173         splash.closeSplash();
    174194
    175195        if (((!args.containsKey("no-maximize") && !args.containsKey("geometry")
  • trunk/src/org/openstreetmap/josm/gui/SplashScreen.java

    r2358 r2817  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.AWTEvent;
    76import java.awt.Color;
    87import java.awt.Dimension;
     
    1211import java.awt.Insets;
    1312import java.awt.Toolkit;
    14 import java.awt.event.AWTEventListener;
    1513import java.awt.event.MouseAdapter;
    1614import java.awt.event.MouseEvent;
     
    1816import javax.swing.JLabel;
    1917import javax.swing.JPanel;
     18import javax.swing.JProgressBar;
    2019import javax.swing.JSeparator;
    2120import javax.swing.JWindow;
    22 import javax.swing.SwingUtilities;
    2321import javax.swing.border.Border;
    2422import javax.swing.border.EmptyBorder;
    2523import javax.swing.border.EtchedBorder;
    2624
    27 import org.openstreetmap.josm.actions.AboutAction;
    2825import org.openstreetmap.josm.data.Version;
     26import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     27import org.openstreetmap.josm.gui.progress.ProgressRenderer;
     28import org.openstreetmap.josm.gui.progress.SwingRenderingProgressMonitor;
    2929import org.openstreetmap.josm.tools.ImageProvider;
    3030
     
    3232 * Show a splash screen so the user knows what is happening during startup.
    3333 *
    34  * @author cbrill
    3534 */
    3635public class SplashScreen extends JWindow {
    3736
    38     private JLabel status;
    39     private boolean visible;
     37    private SplashScreenProgressRenderer progressRenderer;
     38    private SwingRenderingProgressMonitor progressMonitor;
    4039
    41     private Runnable closerRunner;
    42 
    43     public SplashScreen(boolean visible) {
     40    public SplashScreen() {
    4441        super();
    45         this.visible=visible;
    46 
    47         if (!visible)
    48             return;
    4942
    5043        // Add a nice border to the main splash screen
     
    8982
    9083        // Add a status message
    91         status = new JLabel();
     84        progressRenderer = new SplashScreenProgressRenderer();
    9285        gbc.gridy = 3;
    93         gbc.insets = new Insets(0, 0, 0, 0);
    94         innerContentPane.add(status, gbc);
    95         setStatus(tr("Initializing"));
     86        gbc.insets = new Insets(5, 5, 10, 5);
     87        innerContentPane.add(progressRenderer, gbc);
     88        progressMonitor = new SwingRenderingProgressMonitor(progressRenderer);
    9689
    9790        pack();
     
    10396                screenSize.height / 2 - (labelSize.height / 2));
    10497
    105         // Method to close the splash screen when being clicked or when closeSplash is called
    106         closerRunner = new Runnable() {
    107             public void run() {
    108                 setVisible(false);
    109                 dispose();
    110             }
    111         };
    112 
    11398        // Add ability to hide splash screen by clicking it
    11499        addMouseListener(new MouseAdapter() {
     100            @Override
    115101            public void mousePressed(MouseEvent event) {
    116                 try {
    117                     closerRunner.run();
    118                 } catch (Exception e) {
    119                     e.printStackTrace();
    120                     // can catch InvocationTargetException
    121                     // can catch InterruptedException
    122                 }
     102                setVisible(false);
    123103            }
    124104        });
    125 
    126         // Hide splashscreen when other window is created
    127         Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.WINDOW_EVENT_MASK);
    128 
    129         setVisible(true);
    130105    }
    131106
    132     private AWTEventListener awtListener = new AWTEventListener() {
    133         public void eventDispatched(AWTEvent event) {
    134             if (event.getSource() != SplashScreen.this) {
    135                 closeSplash();
    136             }
    137         }
    138     };
    139 
    140     /**
    141      * This method sets the status message. It should be called prior to
    142      * actually doing the action.
    143      *
    144      * @param message
    145      *            the message to be displayed
    146      */
    147     public void setStatus(String message) {
    148         if (!visible)
    149             return;
    150         status.setText(message + "...");
     107    public ProgressMonitor getProgressMonitor() {
     108        return progressMonitor;
    151109    }
    152110
    153     /**
    154      * Closes the splashscreen. Call once you are done starting.
    155      */
    156     public void closeSplash() {
    157         if (!visible)
    158             return;
    159         Toolkit.getDefaultToolkit().removeAWTEventListener(awtListener);
    160         try {
    161             SwingUtilities.invokeLater(closerRunner);
    162         } catch (Exception e) {
    163             e.printStackTrace();
    164             // can catch InvocationTargetException
    165             // can catch InterruptedException
     111    static private class SplashScreenProgressRenderer extends JPanel implements ProgressRenderer {
     112        private JLabel lblTaskTitle;
     113        private JLabel lblCustomText;
     114        private JProgressBar progressBar;
     115
     116        protected void build() {
     117            setLayout(new GridBagLayout());
     118            GridBagConstraints gc = new GridBagConstraints();
     119            gc.gridx = 0;
     120            gc.gridy = 0;
     121            gc.fill = GridBagConstraints.HORIZONTAL;
     122            gc.weightx = 1.0;
     123            gc.weighty = 0.0;
     124            gc.insets = new Insets(5,0,0,5);
     125            add(lblTaskTitle = new JLabel(""), gc);
     126
     127            gc.gridx = 0;
     128            gc.gridy = 1;
     129            gc.fill = GridBagConstraints.HORIZONTAL;
     130            gc.weightx = 1.0;
     131            gc.weighty = 0.0;
     132            gc.insets = new Insets(5,0,0,5);
     133            add(lblCustomText = new JLabel(""), gc);
     134
     135            gc.gridx = 0;
     136            gc.gridy = 2;
     137            gc.fill = GridBagConstraints.HORIZONTAL;
     138            gc.weightx = 1.0;
     139            gc.weighty = 0.0;
     140            gc.insets = new Insets(5,0,0,5);
     141            add(progressBar = new JProgressBar(JProgressBar.HORIZONTAL), gc);
     142        }
     143
     144        public SplashScreenProgressRenderer() {
     145            build();
     146        }
     147
     148        public void setCustomText(String message) {
     149            lblCustomText.setText(message);
     150            repaint();
     151        }
     152
     153        public void setIndeterminate(boolean indeterminate) {
     154            progressBar.setIndeterminate(indeterminate);
     155            repaint();
     156        }
     157
     158        public void setMaximum(int maximum) {
     159            progressBar.setMaximum(maximum);
     160            repaint();
     161        }
     162
     163        public void setTaskTitle(String taskTitle) {
     164            lblTaskTitle.setText(taskTitle);
     165            repaint();
     166        }
     167
     168        public void setValue(int value) {
     169            progressBar.setValue(value);
     170            repaint();
    166171        }
    167172    }
  • trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java

    r2745 r2817  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    6 import java.awt.Dimension;
     5import static org.openstreetmap.josm.tools.I18n.trn;
     6
     7import java.awt.BorderLayout;
     8import java.awt.Component;
     9import java.awt.FlowLayout;
     10import java.awt.GridBagConstraints;
    711import java.awt.GridBagLayout;
    8 import java.awt.Rectangle;
     12import java.awt.Insets;
    913import java.awt.event.ActionEvent;
    10 import java.awt.event.ActionListener;
     14import java.awt.event.ComponentAdapter;
     15import java.awt.event.ComponentEvent;
    1116import java.util.Collection;
    1217import java.util.LinkedList;
     18import java.util.List;
     19import java.util.logging.Logger;
    1320
    1421import javax.swing.AbstractAction;
     22import javax.swing.BorderFactory;
    1523import javax.swing.DefaultListModel;
    1624import javax.swing.JButton;
    1725import javax.swing.JLabel;
    1826import javax.swing.JList;
    19 import javax.swing.JTextField;
    2027import javax.swing.JOptionPane;
    2128import javax.swing.JPanel;
    2229import javax.swing.JScrollPane;
    23 import javax.swing.Scrollable;
     30import javax.swing.JTabbedPane;
     31import javax.swing.JTextField;
     32import javax.swing.SwingUtilities;
     33import javax.swing.UIManager;
     34import javax.swing.event.ChangeEvent;
     35import javax.swing.event.ChangeListener;
    2436import javax.swing.event.DocumentEvent;
    2537import javax.swing.event.DocumentListener;
    2638
    2739import org.openstreetmap.josm.Main;
    28 import org.openstreetmap.josm.plugins.PluginDownloader;
    29 import org.openstreetmap.josm.plugins.PluginSelection;
     40import org.openstreetmap.josm.gui.HelpAwareOptionPane;
     41import org.openstreetmap.josm.gui.help.HelpUtil;
     42import org.openstreetmap.josm.gui.preferences.plugin.PluginPreferencesModel;
     43import org.openstreetmap.josm.gui.preferences.plugin.PluginPreferencesPanel;
     44import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
     45import org.openstreetmap.josm.plugins.PluginDownloadTask;
     46import org.openstreetmap.josm.plugins.PluginInformation;
     47import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
     48import org.openstreetmap.josm.plugins.ReadRemotePluginInformationTask;
    3049import org.openstreetmap.josm.tools.GBC;
     50import org.openstreetmap.josm.tools.ImageProvider;
    3151
    3252public class PluginPreference implements PreferenceSetting {
     53    private final static Logger logger = Logger.getLogger(PluginPreference.class.getName());
    3354
    3455    public static class Factory implements PreferenceSettingFactory {
     
    3859    }
    3960
    40     private JPanel plugin;
    41     private JPanel pluginPanel = new NoHorizontalScrollPanel(new GridBagLayout());
    42     private PreferenceTabbedPane gui;
    43     private JScrollPane pluginPane;
    44     private PluginSelection selection = new PluginSelection();
    45     private JTextField txtFilter;
     61    public static String buildDownloadSummary(PluginDownloadTask task) {
     62        Collection<PluginInformation> downloaded = task.getDownloadedPlugins();
     63        Collection<PluginInformation> failed = task.getFailedPlugins();
     64        StringBuilder sb = new StringBuilder();
     65        if (! downloaded.isEmpty()) {
     66            sb.append(trn(
     67                    "The following plugin has been downloaded <strong>successfully</strong>:",
     68                    "The following {0} plugins have been downloaded successfully:",
     69                    downloaded.size(),
     70                    downloaded.size()
     71            ));
     72            sb.append("<ul>");
     73            for(PluginInformation pi: downloaded) {
     74                sb.append("<li>").append(pi.name).append("</li>");
     75            }
     76            sb.append("</ul>");
     77        }
     78        if (! failed.isEmpty()) {
     79            sb.append(trn(
     80                    "Downloading the following plugin has <strong>failed</strong>:",
     81                    "Downloading the following {0} plugins has <strong>failed</strong>:",
     82                    failed.size(),
     83                    failed.size()
     84            ));
     85            sb.append("<ul>");
     86            for(PluginInformation pi: failed) {
     87                sb.append("<li>").append(pi.name).append("</li>");
     88            }
     89            sb.append("</ul>");
     90        }
     91        return sb.toString();
     92    }
     93
     94    private JTextField tfFilter;
     95    private PluginPreferencesPanel pnlPluginPreferences;
     96    private PluginPreferencesModel model;
     97    private JScrollPane spPluginPreferences;
     98
     99    protected JPanel buildSearchFieldPanel() {
     100        JPanel pnl  = new JPanel(new GridBagLayout());
     101        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
     102        GridBagConstraints gc = new GridBagConstraints();
     103
     104        gc.anchor = GridBagConstraints.NORTHWEST;
     105        gc.fill = GridBagConstraints.HORIZONTAL;
     106        gc.weightx = 0.0;
     107        gc.insets = new Insets(0,0,0,3);
     108        pnl.add(new JLabel(tr("Search:")), gc);
     109
     110        gc.gridx = 1;
     111        gc.weightx = 1.0;
     112        pnl.add(tfFilter = new JTextField(), gc);
     113        tfFilter.setToolTipText(tr("Enter a search expression"));
     114        SelectAllOnFocusGainedDecorator.decorate(tfFilter);
     115        tfFilter.getDocument().addDocumentListener(new SearchFieldAdapter());
     116        return pnl;
     117    }
     118
     119    protected JPanel buildActionPanel() {
     120        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
     121
     122        pnl.add(new JButton(new DownloadAvailablePluginsAction()));
     123        pnl.add(new JButton(new UpdateSelectedPluginsAction()));
     124        pnl.add(new JButton(new ConfigureSitesAction()));
     125        return pnl;
     126    }
     127
     128    protected JPanel buildContentPane() {
     129        JPanel pnl = new JPanel(new BorderLayout());
     130        pnl.add(buildSearchFieldPanel(), BorderLayout.NORTH);
     131        model  = new PluginPreferencesModel();
     132        spPluginPreferences = new JScrollPane(pnlPluginPreferences = new PluginPreferencesPanel(model));
     133        spPluginPreferences.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
     134        spPluginPreferences.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
     135        spPluginPreferences.getVerticalScrollBar().addComponentListener(
     136                new ComponentAdapter(){
     137                    @Override
     138                    public void componentShown(ComponentEvent e) {
     139                        spPluginPreferences.setBorder(UIManager.getBorder("ScrollPane.border"));
     140                    }
     141                    @Override
     142                    public void componentHidden(ComponentEvent e) {
     143                        spPluginPreferences.setBorder(null);
     144                    }
     145                }
     146        );
     147
     148        pnl.add(spPluginPreferences, BorderLayout.CENTER);
     149        pnl.add(buildActionPanel(), BorderLayout.SOUTH);
     150        return pnl;
     151    }
    46152
    47153    public void addGui(final PreferenceTabbedPane gui) {
    48         this.gui = gui;
    49         plugin = gui.createPreferenceTab("plugin", tr("Plugins"), tr("Configure available plugins."), false);
    50 
    51         txtFilter = new JTextField();
    52         JLabel lbFilter = new JLabel(tr("Search: "));
    53         lbFilter.setLabelFor(txtFilter);
    54         plugin.add(lbFilter);
    55         plugin.add(txtFilter, GBC.eol().fill(GBC.HORIZONTAL));
    56         txtFilter.getDocument().addDocumentListener(new DocumentListener(){
    57             public void changedUpdate(DocumentEvent e) {
    58                 action();
    59             }
    60 
    61             public void insertUpdate(DocumentEvent e) {
    62                 action();
    63             }
    64 
    65             public void removeUpdate(DocumentEvent e) {
    66                 action();
    67             }
    68 
    69             private void action() {
    70                 selection.drawPanel(pluginPanel);
    71             }
    72         });
    73         plugin.add(GBC.glue(0,10), GBC.eol());
    74 
    75         /* main plugin area */
    76         pluginPane = new JScrollPane(pluginPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    77         pluginPane.setBorder(null);
    78         plugin.add(pluginPane, GBC.eol().fill(GBC.BOTH));
    79         plugin.add(GBC.glue(0,10), GBC.eol());
    80 
    81         /* buttons at the bottom */
    82         JButton morePlugins = new JButton(tr("Download List"));
    83         morePlugins.addActionListener(new ActionListener(){
    84             public void actionPerformed(ActionEvent e) {
    85                 selection.updateDescription(pluginPanel);
    86             }
    87         });
    88         plugin.add(morePlugins, GBC.std().insets(0,0,10,0));
    89 
    90         JButton update = new JButton(tr("Update"));
    91         update.addActionListener(new ActionListener(){
    92             public void actionPerformed(ActionEvent e) {
    93                 selection.update(pluginPanel);
    94             }
    95         });
    96         plugin.add(update, GBC.std().insets(0,0,10,0));
    97 
    98         JButton configureSites = new JButton(tr("Configure Sites..."));
    99         configureSites.addActionListener(new ActionListener(){
    100             public void actionPerformed(ActionEvent e) {
    101                 configureSites();
    102             }
    103         });
    104         plugin.add(configureSites, GBC.std());
    105 
    106         selection.passTxtFilter(txtFilter);
    107         selection.loadPlugins();
    108         selection.drawPanel(pluginPanel);
     154        GridBagConstraints gc = new GridBagConstraints();
     155        gc.weightx = 1.0;
     156        gc.weighty = 1.0;
     157        gc.anchor = GridBagConstraints.NORTHWEST;
     158        gc.fill = GridBagConstraints.BOTH;
     159        gui.plugins.add(buildContentPane(), gc);
     160        pnlPluginPreferences.refreshView();
     161        gui.addChangeListener(new PluginPreferenceActivationListener(gui.plugins));
    109162    }
    110163
     
    113166        p.add(new JLabel(tr("Add JOSM Plugin description URL.")), GBC.eol());
    114167        final DefaultListModel model = new DefaultListModel();
    115         for (String s : PluginDownloader.getSites()) {
     168        for (String s : Main.pref.getPluginSites()) {
    116169            model.addElement(s);
    117170        }
     
    122175            public void actionPerformed(ActionEvent e) {
    123176                String s = JOptionPane.showInputDialog(
    124                         gui,
     177                        JOptionPane.getFrameForComponent(pnlPluginPreferences),
    125178                        tr("Add JOSM Plugin description URL."),
    126179                        tr("Enter URL"),
     
    136189                if (list.getSelectedValue() == null) {
    137190                    JOptionPane.showMessageDialog(
    138                             gui,
     191                            JOptionPane.getFrameForComponent(pnlPluginPreferences),
    139192                            tr("Please select an entry."),
    140193                            tr("Warning"),
     
    159212                if (list.getSelectedValue() == null) {
    160213                    JOptionPane.showMessageDialog(
    161                             gui,
     214                            JOptionPane.getFrameForComponent(pnlPluginPreferences),
    162215                            tr("Please select an entry."),
    163216                            tr("Warning"),
     
    171224        p.add(buttons, GBC.eol());
    172225        int answer = JOptionPane.showConfirmDialog(
    173                 gui,
     226                JOptionPane.getFrameForComponent(pnlPluginPreferences),
    174227                p,
    175228                tr("Configure Plugin Sites"), JOptionPane.OK_CANCEL_OPTION,
     
    181234            sites.add((String)model.getElementAt(i));
    182235        }
    183         PluginDownloader.setSites(sites);
     236        Main.pref.setPluginSites(sites);
     237    }
     238
     239    /**
     240     * Replies the list of plugins waiting for update or download
     241     *
     242     * @return the list of plugins waiting for update or download
     243     */
     244    public List<PluginInformation> getPluginsScheduledForUpdateOrDownload() {
     245        return model.getPluginsScheduledForUpdateOrDownload();
    184246    }
    185247
    186248    public boolean ok() {
    187         return selection.finish();
    188     }
    189 
    190     private static class NoHorizontalScrollPanel extends JPanel implements Scrollable {
    191         public NoHorizontalScrollPanel(GridBagLayout gridBagLayout) {
    192             super(gridBagLayout);
    193         }
    194 
    195         public Dimension getPreferredScrollableViewportSize() {
    196             return super.getPreferredSize();
    197         }
    198 
    199         public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
    200             return 30;
    201         }
    202 
    203         public boolean getScrollableTracksViewportHeight() {
    204             return false;
    205         }
    206 
    207         public boolean getScrollableTracksViewportWidth() {
     249        if (model.isActivePluginsChanged()) {
     250            Main.pref.putCollection("plugins", model.getSelectedPluginNames());
    208251            return true;
    209252        }
    210 
    211         public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
    212             return 10;
     253        return false;
     254    }
     255
     256    public void readLocalPluginInformation() {
     257        final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
     258        Runnable r = new Runnable() {
     259            public void run() {
     260                if (task.isCanceled()) return;
     261                SwingUtilities.invokeLater(new Runnable() {
     262                    public void run() {
     263                        model.setAvailablePlugins(task.getAvailablePlugins());
     264                        pnlPluginPreferences.refreshView();
     265                    }
     266                });
     267            };
     268        };
     269        Main.worker.submit(task);
     270        Main.worker.submit(r);
     271    }
     272
     273    /**
     274     * The action for downloading the list of available plugins
     275     *
     276     */
     277    class DownloadAvailablePluginsAction extends AbstractAction {
     278
     279        public DownloadAvailablePluginsAction() {
     280            putValue(NAME,tr("Download list"));
     281            putValue(SHORT_DESCRIPTION, tr("Download the list of available plugins"));
     282            putValue(SMALL_ICON, ImageProvider.get("download"));
     283        }
     284
     285        public void actionPerformed(ActionEvent e) {
     286            final ReadRemotePluginInformationTask task = new ReadRemotePluginInformationTask(Main.pref.getPluginSites());
     287            Runnable continuation = new Runnable() {
     288                public void run() {
     289                    if (task.isCanceled()) return;
     290                    SwingUtilities.invokeLater(new Runnable() {
     291                        public void run() {
     292                            model.setAvailablePlugins(task.getAvailabePlugins());
     293                            pnlPluginPreferences.refreshView();
     294
     295                        }
     296                    });
     297                }
     298            };
     299            Main.worker.submit(task);
     300            Main.worker.submit(continuation);
     301        }
     302    }
     303
     304    /**
     305     * The action for downloading the list of available plugins
     306     *
     307     */
     308    class UpdateSelectedPluginsAction extends AbstractAction {
     309        public UpdateSelectedPluginsAction() {
     310            putValue(NAME,tr("Update plugins"));
     311            putValue(SHORT_DESCRIPTION, tr("Update the selected plugins"));
     312            putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh"));
     313        }
     314
     315        protected void notifyDownloadResults(PluginDownloadTask task) {
     316            Collection<PluginInformation> downloaded = task.getDownloadedPlugins();
     317            Collection<PluginInformation> failed = task.getFailedPlugins();
     318            StringBuilder sb = new StringBuilder();
     319            sb.append("<html>");
     320            sb.append(buildDownloadSummary(task));
     321            if (!downloaded.isEmpty()) {
     322                sb.append("Please restart JOSM to activate the downloaded plugins.");
     323            }
     324            sb.append("</html>");
     325            HelpAwareOptionPane.showOptionDialog(
     326                    pnlPluginPreferences,
     327                    sb.toString(),
     328                    tr("Update plugins"),
     329                    failed.isEmpty() ? JOptionPane.WARNING_MESSAGE : JOptionPane.INFORMATION_MESSAGE,
     330                            // FIXME: check help topic
     331                            HelpUtil.ht("/Preferences/Plugin")
     332            );
     333        }
     334
     335        public void actionPerformed(ActionEvent e) {
     336            List<PluginInformation> toUpdate = model.getSelectedPlugins();
     337            final PluginDownloadTask task = new PluginDownloadTask(
     338                    pnlPluginPreferences,
     339                    toUpdate,
     340                    tr("Update plugins")
     341            );
     342            Runnable r = new Runnable() {
     343                public void run() {
     344                    if (task.isCanceled())
     345                        return;
     346                    notifyDownloadResults(task);
     347                    model.refreshLocalPluginVersion(task.getDownloadedPlugins());
     348                    pnlPluginPreferences.refreshView();
     349                }
     350            };
     351            Main.worker.submit(task);
     352            Main.worker.submit(r);
     353        }
     354    }
     355
     356
     357    /**
     358     * The action for configuring the plugin download sites
     359     *
     360     */
     361    class ConfigureSitesAction extends AbstractAction {
     362        public ConfigureSitesAction() {
     363            putValue(NAME,tr("Configure sites..."));
     364            putValue(SHORT_DESCRIPTION, tr("Configure the list of sites where plugins are downloaded from"));
     365        }
     366
     367        public void actionPerformed(ActionEvent e) {
     368            configureSites();
     369        }
     370    }
     371
     372    /**
     373     * Listens to the activation of the plugin preferences tab. On activation it
     374     * reloads plugin information from the local file system.
     375     *
     376     */
     377    class PluginPreferenceActivationListener implements ChangeListener {
     378        private Component pane;
     379        public PluginPreferenceActivationListener(Component preferencesPane) {
     380            pane = preferencesPane;
     381        }
     382
     383        public void stateChanged(ChangeEvent e) {
     384            JTabbedPane tp = (JTabbedPane)e.getSource();
     385            if (tp.getSelectedComponent() == pane) {
     386                readLocalPluginInformation();
     387            }
     388        }
     389    }
     390
     391    /**
     392     * Applies the current filter condition in the filter text field to the
     393     * model
     394     */
     395    class SearchFieldAdapter implements DocumentListener {
     396        public void filter() {
     397            String expr = tfFilter.getText().trim();
     398            if (expr.equals("")) {
     399                expr = null;
     400            }
     401            model.filterDisplayedPlugins(expr);
     402            pnlPluginPreferences.refreshView();
     403        }
     404
     405        public void changedUpdate(DocumentEvent arg0) {
     406            filter();
     407        }
     408
     409        public void insertUpdate(DocumentEvent arg0) {
     410            filter();
     411        }
     412
     413        public void removeUpdate(DocumentEvent arg0) {
     414            filter();
    213415        }
    214416    }
  • trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java

    r2745 r2817  
    2323import javax.swing.JScrollPane;
    2424import javax.swing.JTabbedPane;
     25import javax.swing.SwingUtilities;
    2526
    2627import org.openstreetmap.josm.Main;
     28import org.openstreetmap.josm.plugins.PluginDownloadTask;
    2729import org.openstreetmap.josm.plugins.PluginHandler;
     30import org.openstreetmap.josm.plugins.PluginInformation;
    2831import org.openstreetmap.josm.tools.BugReportExceptionHandler;
    2932import org.openstreetmap.josm.tools.GBC;
     
    4750    public final JPanel map = createPreferenceTab("map", I18n.tr("Map Settings"), I18n.tr("Settings for the map projection and data interpretation."));
    4851    public final JPanel audio = createPreferenceTab("audio", I18n.tr("Audio Settings"), I18n.tr("Settings for the audio player and audio markers."));
     52    public final JPanel plugins = createPreferenceTab("plugin", tr("Plugins"), tr("Configure available plugins."), false);
    4953
    5054    public final javax.swing.JTabbedPane displaycontent = new javax.swing.JTabbedPane();
     
    9599    }
    96100
     101    protected PluginPreference getPluginPreference() {
     102        for (PreferenceSetting setting: settings) {
     103            if (setting instanceof PluginPreference)
     104                return (PluginPreference) setting;
     105        }
     106        return null;
     107    }
     108
    97109    public void savePreferences() {
    98         boolean requiresRestart = false;
    99         for (PreferenceSetting setting : settings)
    100         {
    101             if(setting.ok()) {
    102                 requiresRestart = true;
     110
     111        // create a task for downloading plugins if the user has activated, yet not downloaded,
     112        // new plugins
     113        //
     114        final PluginPreference preference = getPluginPreference();
     115        final List<PluginInformation> toDownload = preference.getPluginsScheduledForUpdateOrDownload();
     116        final PluginDownloadTask task;
     117        if (! toDownload.isEmpty()) {
     118            task = new PluginDownloadTask(this, toDownload, tr("Download plugins"));
     119        } else {
     120            task = null;
     121        }
     122
     123        // this is the task which will run *after* the plugins are downloaded
     124        //
     125        final Runnable continuation = new Runnable() {
     126            public void run() {
     127                boolean requiresRestart = false;
     128                if (task != null && !task.isCanceled()) {
     129                    if (!task.getDownloadedPlugins().isEmpty()) {
     130                        requiresRestart = true;
     131                    }
     132                }
     133
     134                for (PreferenceSetting setting : settings) {
     135                    if (setting.ok()) {
     136                        requiresRestart = true;
     137                    }
     138                }
     139
     140                // build the messages. We only display one message, including the status
     141                // information from the plugin download task and - if necessary - a hint
     142                // to restart JOSM
     143                //
     144                StringBuffer sb = new StringBuffer();
     145                sb.append("<html>");
     146                if (task != null && !task.isCanceled()) {
     147                    sb.append(PluginPreference.buildDownloadSummary(task));
     148                }
     149                if (requiresRestart) {
     150                    sb.append(tr("You have to restart JOSM for some settings to take effect."));
     151                }
     152                sb.append("</html>");
     153
     154                // display the message, if necessary
     155                //
     156                if ((task != null && !task.isCanceled()) || requiresRestart) {
     157                    JOptionPane.showMessageDialog(
     158                            Main.parent,
     159                            sb.toString(),
     160                            tr("Warning"),
     161                            JOptionPane.WARNING_MESSAGE
     162                    );
     163                }
     164                Main.parent.repaint();
    103165            }
    104         }
    105         if (requiresRestart) {
    106             JOptionPane.showMessageDialog(
    107                     Main.parent,
    108                     tr("You have to restart JOSM for some settings to take effect."),
    109                     tr("Warning"),
    110                     JOptionPane.WARNING_MESSAGE
     166        };
     167
     168        if (task != null) {
     169            // if we have to launch a plugin download task we do it asynchronously, followed
     170            // by the remaining "save preferences" activites run on the Swing EDT.
     171            //
     172            Main.worker.submit(task);
     173            Main.worker.submit(
     174                    new Runnable() {
     175                        public void run() {
     176                            SwingUtilities.invokeLater(continuation);
     177                        }
     178                    }
    111179            );
    112         }
    113         Main.parent.repaint();
     180        } else {
     181            // no need for asynchronous activities. Simply run the remaining "save preference"
     182            // activities on this thread (we are already on the Swing EDT
     183            //
     184            continuation.run();
     185        }
    114186    }
    115187
  • trunk/src/org/openstreetmap/josm/io/CacheFiles.java

    r2711 r2817  
    5959
    6060    public CacheFiles(String ident, boolean isPlugin) {
    61         String pref = isPlugin ? Main.pref.getPluginsDirFile().getPath() : Main.pref.getPreferencesDir();
     61        String pref = isPlugin ? Main.pref.getPluginsDirectory().getPath() : Main.pref.getPreferencesDir();
    6262
    6363        boolean dir_writeable;
  • trunk/src/org/openstreetmap/josm/plugins/Plugin.java

    r1169 r2817  
    5353     */
    5454    public final String getPluginDir() {
    55         return new File(Main.pref.getPluginsDirFile(), info.name).getPath();
     55        return new File(Main.pref.getPluginsDirectory(), info.name).getPath();
    5656    }
    5757
     
    8181        String pluginDirName = Main.pref.getPreferencesDir()+"plugins/"+info.name+"/";
    8282        File pluginDir = new File(pluginDirName);
    83         if (!pluginDir.exists())
     83        if (!pluginDir.exists()) {
    8484            pluginDir.mkdirs();
     85        }
    8586        FileOutputStream out = new FileOutputStream(pluginDirName+to);
    8687        InputStream in = getClass().getResourceAsStream(from);
    8788        byte[] buffer = new byte[8192];
    88         for(int len = in.read(buffer); len > 0; len = in.read(buffer))
     89        for(int len = in.read(buffer); len > 0; len = in.read(buffer)) {
    8990            out.write(buffer, 0, len);
     91        }
    9092        in.close();
    9193        out.close();
  • trunk/src/org/openstreetmap/josm/plugins/PluginException.java

    r2512 r2817  
    1111 * @author Immanuel.Scholz
    1212 */
    13 public class PluginException extends RuntimeException {
     13public class PluginException extends Exception {
    1414    public final PluginProxy plugin;
    1515    public final String name;
     
    2020        this.name = name;
    2121    }
     22
     23    public PluginException(String name, String message) {
     24        super(message);
     25        this.plugin = null;
     26        this.name = name;
     27    }
     28
     29    public PluginException(String name, Throwable cause) {
     30        super(tr("An error occurred in plugin {0}", name), cause);
     31        this.plugin = null;
     32        this.name = name;
     33    }
    2234}
  • trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java

    r2584 r2817  
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
     6import static org.openstreetmap.josm.tools.I18n.trn;
    67
    78import java.awt.Font;
    89import java.awt.GridBagLayout;
    910import java.awt.event.ActionEvent;
     11import java.io.File;
     12import java.io.FilenameFilter;
    1013import java.net.URL;
    1114import java.net.URLClassLoader;
     
    1417import java.util.Collection;
    1518import java.util.Collections;
     19import java.util.Comparator;
     20import java.util.HashMap;
     21import java.util.HashSet;
     22import java.util.Iterator;
    1623import java.util.LinkedList;
    1724import java.util.List;
    18 import java.util.SortedMap;
    19 import java.util.TreeMap;
     25import java.util.Map;
     26import java.util.Set;
    2027import java.util.Map.Entry;
     28import java.util.concurrent.ExecutionException;
     29import java.util.concurrent.ExecutorService;
     30import java.util.concurrent.Executors;
     31import java.util.concurrent.Future;
    2132
    2233import javax.swing.AbstractAction;
     
    3748import org.openstreetmap.josm.gui.download.DownloadSelection;
    3849import 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;
    3953import org.openstreetmap.josm.tools.GBC;
    4054import org.openstreetmap.josm.tools.ImageProvider;
    4155
     56/**
     57 * PluginHandler is basically a collection of static utility functions used to bootstrap
     58 * and manage the loaded plugins.
     59 *
     60 */
    4261public class PluginHandler {
    4362
    44     public static String [] oldplugins = new String[] {"mappaint", "unglueplugin",
    45                 "lang-de", "lang-en_GB", "lang-fr", "lang-it", "lang-pl", "lang-ro",
    46                 "lang-ru", "ewmsplugin", "ywms", "tways-0.2", "geotagged", "landsat",
    47                 "namefinder", "waypoints", "slippy_map_chooser", "tcx-support", "usertools",
    48                 "AgPifoJ", "utilsplugin"};
    49     public static String [] unmaintained = new String[] {"gpsbabelgui", "Intersect_way"};
     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"};
    5070
    5171    /**
     
    5373     */
    5474    public final static Collection<PluginProxy> pluginList = new LinkedList<PluginProxy>();
    55     /**
    56      * Load all plugins specified in preferences. If the parameter is
    57      * <code>true</code>, all early plugins are loaded (before constructor).
    58      */
    59     public static void loadPlugins(boolean early) {
    60         List<String> plugins = new LinkedList<String>();
    61         Collection<String> cp = Main.pref.getCollection("plugins", null);
    62         if (cp != null) {
    63             plugins.addAll(cp);
    64         }
    65         if (System.getProperty("josm.plugins") != null) {
    66             plugins.addAll(Arrays.asList(System.getProperty("josm.plugins").split(",")));
    67         }
    68 
    69         for (String p : oldplugins) {
     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) {
    7088            if (plugins.contains(p)) {
    7189                plugins.remove(p);
    7290                Main.pref.removeFromCollection("plugins", p);
    73                 JOptionPane.showMessageDialog(
    74                         Main.parent,
    75                         tr("Plugin {0} is no longer necessary and has been deactivated.", p),
    76                         tr("Warning"),
    77                         JOptionPane.WARNING_MESSAGE
    78                 );
    79             }
    80         }
    81         if(early)
    82         {
    83             for (String p : unmaintained) {
    84                 if (plugins.contains(p) && disablePlugin(tr("<html>Loading of {0} plugin was requested."
    85                         +"<br>This plugin is no longer developed and very likely will produce errors."
    86                         +"<br>It should be disabled.<br>Delete from preferences?</html>", p), p)) {
    87                     plugins.remove(p);
    88                 }
    89             }
    90         }
    91 
    92         if (plugins.isEmpty())
     91                removedPlugins.add(p);
     92            }
     93        }
     94        if (removedPlugins.isEmpty())
    9395            return;
    9496
    95         if(early)
    96         {
    97             String doUpdate = null;
    98             String check = null;
    99             int v = Version.getInstance().getVersion();
    100             if(Main.pref.getInteger("pluginmanager.version", 0) < v)
    101             {
    102                 doUpdate = tr("You updated your JOSM software.\nTo prevent problems the plugins should be updated as well.\n"
    103                         + "Update plugins now?");
    104                 check = "pluginmanger.version";
    105             }
    106             else
    107             {
    108                 long tim = System.currentTimeMillis();
    109                 long last = Main.pref.getLong("pluginmanager.lastupdate", 0);
    110                 Integer maxTime = Main.pref.getInteger("pluginmanager.warntime", 60);
    111                 long d = (tim - last)/(24*60*60*1000l);
    112                 if ((last <= 0) || (maxTime <= 0)) {
    113                     Main.pref.put("pluginmanager.lastupdate",Long.toString(tim));
    114                 } else if (d > maxTime) {
    115                     doUpdate = tr("Last plugin update more than {0} days ago.", d);
    116                     check = "pluginmanager.time";
    117                 }
    118             }
    119             if(doUpdate != null)
    120             {
    121                 ExtendedDialog dialog = new ExtendedDialog(
    122                         Main.parent,
    123                         tr("Update plugins"),
    124                         new String[] {tr("Update plugins"), tr("Skip update")}
    125                 );
    126                 dialog.setContent(doUpdate);
    127                 dialog.toggleEnable(check);
    128                 dialog.setButtonIcons( new String[] {"dialogs/refresh.png", "cancel.png"});
    129                 dialog.configureContextsensitiveHelp(ht("/Plugin/AutomaticUpdate"), true /* show help button */);
    130                 dialog.showDialog();
    131                 if(dialog.getValue() == 1) {
    132                     new PluginSelection().update();
    133                 }
    134             }
    135         }
    136 
    137         SortedMap<Integer, Collection<PluginInformation>> p = new TreeMap<Integer, Collection<PluginInformation>>();
    138         for (String pluginName : plugins) {
    139             PluginInformation info = PluginInformation.findPlugin(pluginName);
    140             if (info != null) {
    141                 if (info.early != early) {
    142                     continue;
    143                 }
    144                 int josmVersion = Version.getInstance().getVersion();
    145                 if (info.mainversion > josmVersion && josmVersion != Version.JOSM_UNKNOWN_VERSION) {
    146                     JOptionPane.showMessageDialog(
    147                             Main.parent,
    148                             tr("Plugin {0} requires JOSM update to version {1}.", pluginName,
    149                                     info.mainversion),
    150                                     tr("Warning"),
    151                                     JOptionPane.WARNING_MESSAGE
    152                     );
    153                     continue;
    154                 }
    155 
    156                 if(info.requires != null)
    157                 {
    158                     String warn = null;
    159                     for(String n : info.requires.split(";"))
    160                     {
    161                         if(!plugins.contains(n))
    162                         { warn = n; break; }
    163                     }
    164                     if(warn != null)
    165                     {
    166                         JOptionPane.showMessageDialog(Main.parent,
    167                                 tr("Plugin {0} is required by plugin {1} but was not found.",
    168                                         warn, pluginName),
    169                                         tr("Error"),
    170                                         JOptionPane.ERROR_MESSAGE
    171                         );
    172                         continue;
    173                     }
    174                 }
    175                 if (!p.containsKey(info.stage)) {
    176                     p.put(info.stage, new LinkedList<PluginInformation>());
    177                 }
    178                 p.get(info.stage).add(info);
    179             } else if(early) {
    180                 JOptionPane.showMessageDialog(
    181                         Main.parent,
    182                         tr("Plugin not found: {0}.", pluginName),
    183                         tr("Error"),
    184                         JOptionPane.ERROR_MESSAGE
    185                 );
    186             }
    187         }
    188 
     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) {
    189276        // iterate all plugins and collect all libraries of all plugins:
    190         List<URL> allPluginLibraries = new ArrayList<URL>();
    191         for (Collection<PluginInformation> c : p.values()) {
    192             for (PluginInformation info : c) {
    193                 allPluginLibraries.addAll(info.libraries);
    194             }
    195         }
     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
    196289        // create a classloader for all plugins:
    197290        URL[] jarUrls = new URL[allPluginLibraries.size()];
    198291        jarUrls = allPluginLibraries.toArray(jarUrls);
    199292        URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader());
    200         ImageProvider.sources.add(0, pluginClassLoader);
    201 
    202         for (Collection<PluginInformation> c : p.values()) {
    203             for (PluginInformation info : c) {
    204                 try {
    205                     Class<?> klass = info.loadClass(pluginClassLoader);
    206                     if (klass != null) {
    207                         System.out.println("loading "+info.name);
    208                         pluginList.add(info.load(klass));
     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                        }
    209344                    }
    210                 } catch (Throwable e) {
    211                     e.printStackTrace();
    212                     disablePlugin(tr("Could not load plugin {0}. Delete from preferences?", info.name), info.name);
    213                 }
    214             }
    215         }
    216     }
    217     public static boolean disablePlugin(String reason, String name)
    218     {
     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) {
    219573        ExtendedDialog dialog = new ExtendedDialog(
    220574                Main.parent,
    221575                tr("Disable plugin"),
    222                 new String[] {tr("Disable plugin"), tr("Keep plugin")}
     576                new String[] {
     577                    tr("Disable plugin"), tr("Keep plugin")
     578                }
    223579        );
    224580        dialog.setContent(reason);
    225         dialog.setButtonIcons( new String[] {"dialogs/delete.png", "cancel.png"});
     581        dialog.setButtonIcons(new String[] { "dialogs/delete.png", "cancel.png" });
    226582        dialog.showDialog();
    227         int result = dialog.getValue();
    228 
    229         if(result == 1)
    230         {
    231             Main.pref.removeFromCollection("plugins", name);
    232             return true;
    233         }
    234         return false;
    235     }
    236 
    237     public static void setMapFrame(MapFrame old, MapFrame map) {
     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) {
    238593        for (PluginProxy plugin : pluginList) {
    239594            plugin.mapFrameInitialized(old, map);
     
    248603    }
    249604
    250     public static void addDownloadSelection(List<DownloadSelection> downloadSelections)
    251     {
     605    public static void addDownloadSelection(List<DownloadSelection> downloadSelections) {
    252606        for (PluginProxy p : pluginList) {
    253607            p.addDownloadSelection(downloadSelections);
    254608        }
    255609    }
    256     public static void getPreferenceSetting(Collection<PreferenceSettingFactory> settings)
    257     {
     610
     611    public static void getPreferenceSetting(Collection<PreferenceSettingFactory> settings) {
    258612        for (PluginProxy plugin : pluginList) {
    259613            settings.add(new PluginPreferenceFactory(plugin));
     
    261615    }
    262616
    263     public static void earlyCleanup()
    264     {
    265         if (!PluginDownloader.moveUpdatedPlugins()) {
    266             JOptionPane.showMessageDialog(
    267                     Main.parent,
    268                     tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."),
    269                     tr("Plugins"), JOptionPane.ERROR_MESSAGE);
    270         }
    271     }
    272     public static Boolean checkException(Throwable e)
     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)
    273652    {
    274653        PluginProxy plugin = null;
     
    350729        return false;
    351730    }
    352     public static String getBugReportText()
    353     {
     731
     732    public static String getBugReportText() {
    354733        String text = "";
    355734        String pl = Main.pref.getCollectionAsString("plugins");
    356         if(pl != null && pl.length() != 0) {
    357             text += "Plugins: "+pl+"\n";
     735        if (pl != null && pl.length() != 0) {
     736            text += "Plugins: " + pl + "\n";
    358737        }
    359738        for (final PluginProxy pp : pluginList) {
    360             text += "Plugin " + pp.info.name + (pp.info.version != null && !pp.info.version.equals("") ? " Version: "+pp.info.version+"\n" : "\n");
     739            text += "Plugin "
     740                + pp.info.name
     741                + (pp.info.version != null && !pp.info.version.equals("") ? " Version: " + pp.info.version + "\n"
     742                        : "\n");
    361743        }
    362744        return text;
    363745    }
    364     public static JPanel getInfoPanel()
    365     {
     746
     747    public static JPanel getInfoPanel() {
    366748        JPanel pluginTab = new JPanel(new GridBagLayout());
    367749        for (final PluginProxy p : pluginList) {
    368             String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : "");
     750            String name = p.info.name
     751            + (p.info.version != null && !p.info.version.equals("") ? " Version: " + p.info.version : "");
    369752            pluginTab.add(new JLabel(name), GBC.std());
    370753            pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
    371             pluginTab.add(new JButton(new AbstractAction(tr("Information")){
     754            pluginTab.add(new JButton(new AbstractAction(tr("Information")) {
    372755                public void actionPerformed(ActionEvent event) {
    373756                    StringBuilder b = new StringBuilder();
    374                     for (Entry<String,String> e : p.info.attr.entrySet()) {
     757                    for (Entry<String, String> e : p.info.attr.entrySet()) {
    375758                        b.append(e.getKey());
    376759                        b.append(": ");
     
    378761                        b.append("\n");
    379762                    }
    380                     JTextArea a = new JTextArea(10,40);
     763                    JTextArea a = new JTextArea(10, 40);
    381764                    a.setEditable(false);
    382765                    a.setText(b.toString());
    383                     JOptionPane.showMessageDialog(
    384                             Main.parent,
    385                             new JScrollPane(a),
    386                             tr("Plugin information"),
    387                             JOptionPane.INFORMATION_MESSAGE
    388                     );
     766                    JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a), tr("Plugin information"),
     767                            JOptionPane.INFORMATION_MESSAGE);
    389768                }
    390769            }), GBC.eol());
    391770
    392             JTextArea description = new JTextArea((p.info.description==null? tr("no description available"):p.info.description));
     771            JTextArea description = new JTextArea((p.info.description == null ? tr("no description available")
     772                    : p.info.description));
    393773            description.setEditable(false);
    394774            description.setFont(new JLabel().getFont().deriveFont(Font.ITALIC));
    395775            description.setLineWrap(true);
    396776            description.setWrapStyleWord(true);
    397             description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
     777            description.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0));
    398778            description.setBackground(UIManager.getColor("Panel.background"));
    399779
  • trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java

    r2620 r2817  
    4343    public int stage = 50;
    4444    public String version = null;
     45    public String localversion = null;
    4546    public String downloadlink = null;
    4647    public List<URL> libraries = new LinkedList<URL>();
     
    6061     * @param file the plugin jar file.
    6162     */
    62     public PluginInformation(File file) {
     63    public PluginInformation(File file) throws PluginException{
    6364        this(file, file.getName().substring(0, file.getName().length()-4));
    6465    }
    6566
    66     public PluginInformation(File file, String name) {
     67    public PluginInformation(File file, String name) throws PluginException{
    6768        this.name = name;
    6869        this.file = file;
     70        JarInputStream jar = null;
    6971        try {
    70             JarInputStream jar = new JarInputStream(new FileInputStream(file));
     72            jar = new JarInputStream(new FileInputStream(file));
    7173            Manifest manifest = jar.getManifest();
    7274            if (manifest == null)
    73                 throw new IOException(file+" contains no manifest.");
     75                throw new PluginException(name, tr("The plugin file ''{0}'' doesn't include a Manifest.", file.toString()));
    7476            scanManifest(manifest, false);
    7577            libraries.add(0, fileToURL(file));
    76             jar.close();
    7778        } catch (IOException e) {
    78             throw new PluginException(null, name, e);
    79         }
    80     }
    81 
    82     public PluginInformation(InputStream manifestStream, String name, String url) {
     79            throw new PluginException(name, e);
     80        } finally {
     81            if (jar != null) {
     82                try {
     83                    jar.close();
     84                } catch(IOException e) { /* ignore */ }
     85            }
     86        }
     87    }
     88
     89    public PluginInformation(InputStream manifestStream, String name, String url) throws PluginException {
    8390        this.name = name;
    8491        try {
     
    9097            scanManifest(manifest, url != null);
    9198        } catch (IOException e) {
    92             throw new PluginException(null, name, e);
     99            throw new PluginException(name, e);
    93100        }
    94101    }
     
    167174    }
    168175
    169     public String getLinkDescription()
    170     {
    171         String d = description == null ? tr("no description available") : description;
    172         if(link != null) {
    173             d += " <A HREF=\""+link+"\">"+tr("More details")+"</A>";
    174         }
    175         return d;
     176    /**
     177     * Replies the description as HTML document, including a link to a web page with
     178     * more information, provided such a link is available.
     179     *
     180     * @return the description as HTML document
     181     */
     182    public String getDescriptionAsHtml() {
     183        StringBuilder sb = new StringBuilder();
     184        sb.append("<html><body>");
     185        sb.append(description == null ? tr("no description available") : description);
     186        if (link != null) {
     187            sb.append(" <a href=\"").append(link).append("\">").append(tr("More info...")).append("</a>");
     188        }
     189        sb.append("</body></html>");
     190        return sb.toString();
    176191    }
    177192
     
    179194     * Load and instantiate the plugin
    180195     */
    181     public PluginProxy load(Class<?> klass) {
     196    public PluginProxy load(Class<?> klass) throws PluginException{
    182197        try {
    183198            currentPluginInitialization = this;
    184199            return new PluginProxy(klass.newInstance(), this);
    185         } catch (Exception e) {
    186             throw new PluginException(null, name, e);
     200        } catch(IllegalAccessException e) {
     201            throw new PluginException(name, e);
     202        } catch (InstantiationException e) {
     203            throw new PluginException(name, e);
    187204        }
    188205    }
     
    191208     * Load the class of the plugin
    192209     */
    193     public Class<?> loadClass(ClassLoader classLoader) {
     210    public Class<?> loadClass(ClassLoader classLoader) throws PluginException {
    194211        if (className == null)
    195212            return null;
    196         try {
     213        try{
    197214            Class<?> realClass = Class.forName(className, true, classLoader);
    198215            return realClass;
    199         } catch (Exception e) {
    200             throw new PluginException(null, name, e);
     216        } catch (ClassNotFoundException e) {
     217            throw new PluginException(name, e);
    201218        }
    202219    }
     
    272289        return all;
    273290    }
     291
     292    /**
     293     * Replies true if the plugin with the given information is most likely outdated with
     294     * respect to the referenceVersion.
     295     *
     296     * @param referenceVersion the reference version. Can be null if we don't know a
     297     * reference version
     298     *
     299     * @return true, if the plugin needs to be updated; false, otherweise
     300     */
     301    public boolean isUpdateRequired(String referenceVersion) {
     302        if (this.version == null && referenceVersion!= null)
     303            return true;
     304        if (this.version != null && !this.version.equals(referenceVersion))
     305            return true;
     306        return false;
     307    }
     308
     309    /**
     310     * Replies true if this this plugin should be updated/downloaded because either
     311     * it is not available locally (its local version is null) or its local version is
     312     * older than the available version on the server.
     313     *
     314     * @return true if the plugin should be updated
     315     */
     316    public boolean isUpdateRequired() {
     317        if (this.localversion == null) return false;
     318        return isUpdateRequired(this.localversion);
     319    }
     320
     321    protected boolean matches(String filter, String value) {
     322        if (filter == null) return true;
     323        if (value == null) return false;
     324        return value.toLowerCase().contains(filter.toLowerCase());
     325    }
     326
     327    /**
     328     * Replies true if either the name, the description, or the version match (case insensitive)
     329     * one of the words in filter. Replies true if filter is null.
     330     *
     331     * @param filter the filter expression
     332     * @return true if this plugin info matches with the filter
     333     */
     334    public boolean matches(String filter) {
     335        if (filter == null) return true;
     336        String words[] = filter.split("\\s+");
     337        for (String word: words) {
     338            if (matches(word, name)
     339                    || matches(word, description)
     340                    || matches(word, version)
     341                    || matches(word, localversion))
     342                return true;
     343        }
     344        return false;
     345    }
     346
     347    /**
     348     * Replies the name of the plugin
     349     */
     350    public String getName() {
     351        return name;
     352    }
     353
     354    /**
     355     * Sets the name
     356     * @param name
     357     */
     358    public void setName(String name) {
     359        this.name = name;
     360    }
     361
    274362}
Note: See TracChangeset for help on using the changeset viewer.