Changeset 1326 in josm


Ignore:
Timestamp:
Jan 23, 2009 10:22:10 PM (4 years ago)
Author:
stoecker
Message:

reworked plugin handling a lot, more to come

Location:
trunk/src/org/openstreetmap/josm
Files:
3 added
11 edited

Legend:

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

    r1307 r1326  
    1212import java.net.URI; 
    1313import java.net.URISyntaxException; 
    14 import java.net.URL; 
    15 import java.net.URLClassLoader; 
    16 import java.util.ArrayList; 
    17 import java.util.Arrays; 
    1814import java.util.Collection; 
    19 import java.util.LinkedList; 
    20 import java.util.List; 
    2115import java.util.Locale; 
    2216import java.util.Map; 
    23 import java.util.SortedMap; 
    2417import java.util.StringTokenizer; 
    25 import java.util.TreeMap; 
    2618import java.util.concurrent.Executor; 
    2719import java.util.concurrent.Executors; 
     
    3426import javax.swing.UIManager; 
    3527 
    36 import org.openstreetmap.josm.actions.AboutAction; 
    3728import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask; 
    3829import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; 
     
    5748import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference; 
    5849import org.openstreetmap.josm.gui.preferences.ToolbarPreferences; 
    59 import org.openstreetmap.josm.plugins.PluginInformation; 
    60 import org.openstreetmap.josm.plugins.PluginProxy; 
     50import org.openstreetmap.josm.plugins.PluginHandler; 
    6151import org.openstreetmap.josm.tools.ImageProvider; 
    6252import org.openstreetmap.josm.tools.OsmUrlToBounds; 
     
    10292     */ 
    10393    public static MapFrame map; 
    104     /** 
    105      * All installed and loaded plugins (resp. their main classes) 
    106      */ 
    107     public final static Collection<PluginProxy> plugins = new LinkedList<PluginProxy>(); 
    10894    /** 
    10995     * The dialog that gets displayed during background task execution. 
     
    164150        redoUndoListener.commandChanged(0,0); 
    165151 
    166         for (PluginProxy plugin : plugins) 
    167             plugin.mapFrameInitialized(old, map); 
    168     } 
    169  
    170     /** 
    171      * Set the layer menu (changed when active layer changes). 
    172      */ 
    173     public final void setLayerMenu(Component[] entries) { 
    174         //if (entries == null || entries.length == 0) 
    175             //menu.layerMenu.setVisible(false); 
    176         //else { 
    177             //menu.layerMenu.removeAll(); 
    178             //for (Component c : entries) 
    179                 //menu.layerMenu.add(c); 
    180             //menu.layerMenu.setVisible(true); 
    181         //} 
     152        PluginHandler.setMapFrame(old, map); 
    182153    } 
    183154 
     
    197168        this(null); 
    198169    } 
    199      
     170 
    200171    public Main(SplashScreen splash) { 
    201172        main = this; 
     
    205176        if(splash != null) splash.setStatus(tr("Download \"Message of the day\"")); 
    206177        panel.add(new GettingStarted(), BorderLayout.CENTER); 
    207          
     178 
    208179        if(splash != null) splash.setStatus(tr("Creating main GUI")); 
    209180        menu = new MainMenu(); 
     
    214185        contentPane.add(toolbar.control, BorderLayout.NORTH); 
    215186 
    216         contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1, Shortcut.GROUP_DIRECT).getKeyStroke(), "Help"); 
     187        contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) 
     188        .put(Shortcut.registerShortcut("system:help", tr("Help"), 
     189        KeyEvent.VK_F1, Shortcut.GROUP_DIRECT).getKeyStroke(), "Help"); 
    217190        contentPane.getActionMap().put("Help", menu.help); 
    218191 
     
    224197        toolbar.control.updateUI(); 
    225198        contentPane.updateUI(); 
    226     } 
    227  
    228     /** 
    229      * Load all plugins specified in preferences. If the parameter is 
    230      * <code>true</code>, all early plugins are loaded (before constructor). 
    231      */ 
    232     public static void loadPlugins(boolean early) { 
    233         List<String> plugins = new LinkedList<String>(); 
    234         Collection<String> cp = Main.pref.getCollection("plugins", null); 
    235         if (cp != null) 
    236             plugins.addAll(cp); 
    237         if (System.getProperty("josm.plugins") != null) 
    238             plugins.addAll(Arrays.asList(System.getProperty("josm.plugins").split(","))); 
    239  
    240         String [] oldplugins = new String[] {"mappaint", "unglueplugin", 
    241         "lang-de", "lang-en_GB", "lang-fr", "lang-it", "lang-pl", "lang-ro", 
    242         "lang-ru", "ewmsplugin", "ywms", "tways-0.2", "geotagged", "landsat"}; 
    243         for (String p : oldplugins) { 
    244             if (plugins.contains(p)) { 
    245                 plugins.remove(p); 
    246                 Main.pref.removeFromCollection("plugins", p); 
    247                 System.out.println(tr("Warning - loading of {0} plugin was requested. This plugin is no longer required.", p)); 
    248             } 
    249         } 
    250  
    251         if (plugins.isEmpty()) 
    252             return; 
    253  
    254         SortedMap<Integer, Collection<PluginInformation>> p = new TreeMap<Integer, Collection<PluginInformation>>(); 
    255         for (String pluginName : plugins) { 
    256             PluginInformation info = PluginInformation.findPlugin(pluginName); 
    257             if (info != null) { 
    258                 if (info.early != early) 
    259                     continue; 
    260                 if (info.mainversion != null) { 
    261                     int requiredJOSMVersion = 0; 
    262                     try { 
    263                         requiredJOSMVersion = Integer.parseInt(info.mainversion); 
    264                     } catch(NumberFormatException e) { 
    265                         e.printStackTrace(); 
    266                     } 
    267                     if (requiredJOSMVersion > AboutAction.getVersionNumber()) { 
    268                         JOptionPane.showMessageDialog(Main.parent, tr("Plugin requires JOSM update: {0}.", pluginName)); 
    269                         continue; 
    270                     } 
    271                 } 
    272                 if (!p.containsKey(info.stage)) 
    273                     p.put(info.stage, new LinkedList<PluginInformation>()); 
    274                 p.get(info.stage).add(info); 
    275             } else { 
    276                 if (early) 
    277                     System.out.println("Plugin not found: "+pluginName); // do not translate 
    278                 else 
    279                     JOptionPane.showMessageDialog(Main.parent, tr("Plugin not found: {0}.", pluginName)); 
    280             } 
    281         } 
    282  
    283         if (!early) { 
    284             long tim = System.currentTimeMillis(); 
    285             long last = Main.pref.getLong("pluginmanager.lastupdate", 0); 
    286             Integer maxTime = Main.pref.getInteger("pluginmanager.warntime", 30); 
    287             long d = (tim - last)/(24*60*60*1000l); 
    288             if ((last <= 0) || (maxTime <= 0)) { 
    289                 Main.pref.put("pluginmanager.lastupdate",Long.toString(tim)); 
    290             } else if (d > maxTime) { 
    291                 JOptionPane.showMessageDialog(Main.parent, 
    292                    "<html>" + 
    293                    tr("Last plugin update more than {0} days ago.", d) + 
    294                    "<br><em>" + 
    295                    tr("(You can change the number of days after which this warning appears<br>by setting the config option 'pluginmanager.warntime'.)") + 
    296                    "</html>"); 
    297             } 
    298         } 
    299  
    300         // iterate all plugins and collect all libraries of all plugins: 
    301         List<URL> allPluginLibraries = new ArrayList<URL>(); 
    302         for (Collection<PluginInformation> c : p.values()) 
    303             for (PluginInformation info : c) 
    304                 allPluginLibraries.addAll(info.libraries); 
    305         // create a classloader for all plugins: 
    306         URL[] jarUrls = new URL[allPluginLibraries.size()]; 
    307         jarUrls = allPluginLibraries.toArray(jarUrls); 
    308         URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader()); 
    309         ImageProvider.sources.add(0, pluginClassLoader); 
    310  
    311         for (Collection<PluginInformation> c : p.values()) { 
    312             for (PluginInformation info : c) { 
    313                 try { 
    314                     Class<?> klass = info.loadClass(pluginClassLoader); 
    315                     if (klass != null) { 
    316                         System.out.println("loading "+info.name); 
    317                         Main.plugins.add(info.load(klass)); 
    318                     } 
    319                 } catch (Throwable e) { 
    320                     e.printStackTrace(); 
    321                     boolean remove = true; 
    322                     if (early) 
    323                         System.out.println("Could not load plugin: "+info.name+" - deleted from preferences"); // do not translate 
    324                     else { 
    325                         int answer = JOptionPane.showConfirmDialog(Main.parent, 
    326                             tr("Could not load plugin {0}. Delete from preferences?", info.name, 
    327                             JOptionPane.YES_NO_OPTION)); 
    328                         if (answer != JOptionPane.OK_OPTION) { 
    329                             remove = false; 
    330                         } 
    331                     } 
    332                     if (remove) { 
    333                         plugins.remove(info.name); 
    334                         String plist = null; 
    335                         for (String pn : plugins) { 
    336                             if (plist==null) plist=""; else plist=plist+","; 
    337                             plist=plist+pn; 
    338                         } 
    339                         Main.pref.put("plugins", plist); 
    340                     } 
    341                 } 
    342             } 
    343         } 
    344199    } 
    345200 
     
    370225     */ 
    371226    public static final JPanel contentPane = new JPanel(new BorderLayout()); 
    372  
    373227 
    374228    /////////////////////////////////////////////////////////////////////////// 
  • trunk/src/org/openstreetmap/josm/actions/AboutAction.java

    r1301 r1326  
    1515import java.net.MalformedURLException; 
    1616import java.net.URL; 
    17 import java.util.Map.Entry; 
    1817import java.util.regex.Matcher; 
    1918import java.util.regex.Pattern; 
    2019 
    21 import javax.swing.AbstractAction; 
    2220import javax.swing.BorderFactory; 
    23 import javax.swing.Box; 
    24 import javax.swing.JButton; 
    2521import javax.swing.JLabel; 
    2622import javax.swing.JOptionPane; 
     
    2925import javax.swing.JTabbedPane; 
    3026import javax.swing.JTextArea; 
    31 import javax.swing.UIManager; 
    3227 
    3328import org.openstreetmap.josm.Main; 
    34 import org.openstreetmap.josm.plugins.PluginProxy; 
     29import org.openstreetmap.josm.plugins.PluginHandler; 
    3530import org.openstreetmap.josm.tools.GBC; 
    3631import org.openstreetmap.josm.tools.ImageProvider; 
     
    145140        about.addTab(tr("Revision"), createScrollPane(revision)); 
    146141        about.addTab(tr("Contribution"), createScrollPane(contribution)); 
    147  
    148         JPanel pluginTab = new JPanel(new GridBagLayout()); 
    149         for (final PluginProxy p : Main.plugins) { 
    150             String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : ""); 
    151             pluginTab.add(new JLabel(name), GBC.std()); 
    152             pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL)); 
    153             pluginTab.add(new JButton(new AbstractAction(tr("Information")){ 
    154                 public void actionPerformed(ActionEvent event) { 
    155                     StringBuilder b = new StringBuilder(); 
    156                     for (Entry<String,String> e : p.info.attr.entrySet()) { 
    157                         b.append(e.getKey()); 
    158                         b.append(": "); 
    159                         b.append(e.getValue()); 
    160                         b.append("\n"); 
    161                     } 
    162                     JTextArea a = new JTextArea(10,40); 
    163                     a.setEditable(false); 
    164                     a.setText(b.toString()); 
    165                     JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a)); 
    166                 } 
    167             }), GBC.eol()); 
    168  
    169             JTextArea description = new JTextArea((p.info.description==null? tr("no description available"):p.info.description)); 
    170             description.setEditable(false); 
    171             description.setFont(new JLabel().getFont().deriveFont(Font.ITALIC)); 
    172             description.setLineWrap(true); 
    173             description.setWrapStyleWord(true); 
    174             description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0)); 
    175             description.setBackground(UIManager.getColor("Panel.background")); 
    176              
    177             pluginTab.add(description, GBC.eop().fill(GBC.HORIZONTAL)); 
    178         } 
    179         about.addTab(tr("Plugins"), new JScrollPane(pluginTab)); 
     142        about.addTab(tr("Plugins"), new JScrollPane(PluginHandler.getInfoPanel())); 
    180143 
    181144        about.setPreferredSize(new Dimension(500,300)); 
    182145 
    183146        JOptionPane.showMessageDialog(Main.parent, about, tr("About JOSM..."), 
    184                 JOptionPane.INFORMATION_MESSAGE, ImageProvider.get("logo")); 
     147        JOptionPane.INFORMATION_MESSAGE, ImageProvider.get("logo")); 
    185148    } 
    186149 
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r1245 r1326  
    11// License: GPL. Copyright 2007 by Immanuel Scholz and others 
    22package org.openstreetmap.josm.data; 
     3 
     4import static org.openstreetmap.josm.tools.I18n.tr; 
    35 
    46import java.awt.Color; 
     
    2224import java.util.regex.Pattern; 
    2325 
     26import javax.swing.JOptionPane; 
     27 
    2428import org.openstreetmap.josm.Main; 
    2529import org.openstreetmap.josm.actions.AboutAction; 
     
    298302            properties.put(line.substring(0,i), line.substring(i+1)); 
    299303        } 
    300         if (!errLines.isEmpty()) { 
    301             throw new IOException("Malformed config file at lines " + errLines); 
    302         } 
     304        if (!errLines.isEmpty()) 
     305            throw new IOException(tr("Malformed config file at lines {0}", errLines)); 
    303306        setSystemProperties(); 
     307    } 
     308 
     309    public void init(Boolean reset) 
     310    { 
     311        // get the preferences. 
     312        File prefDir = getPreferencesDirFile(); 
     313        if (prefDir.exists()) { 
     314            if(!prefDir.isDirectory()) { 
     315                JOptionPane.showMessageDialog(null, tr("Cannot open preferences directory: {0}",Main.pref.getPreferencesDir())); 
     316                return; 
     317            } 
     318        } 
     319        else 
     320            prefDir.mkdirs(); 
     321 
     322        if (!new File(getPreferencesDir()+"preferences").exists()) 
     323            resetToDefault(); 
     324 
     325        try { 
     326            if (reset) 
     327                resetToDefault(); 
     328            else 
     329                load(); 
     330        } catch (final IOException e1) { 
     331            e1.printStackTrace(); 
     332            String backup = getPreferencesDir() + "preferences.bak"; 
     333            JOptionPane.showMessageDialog(null, tr("Preferences file had errors. Making backup of old one to {0}.", backup)); 
     334            new File(getPreferencesDir() + "preferences").renameTo(new File(backup)); 
     335            save(); 
     336        } 
    304337    } 
    305338 
     
    448481            /* handle space separated stuff - remove in future */ 
    449482            else if(s.indexOf(' ') >= 0) 
    450                 return Arrays.asList(s.split(",")); 
     483                return Arrays.asList(s.split(" ")); 
    451484            else 
    452485                return Arrays.asList(s.split(";")); 
  • trunk/src/org/openstreetmap/josm/gui/MainApplication.java

    r1286 r1326  
    66import static org.openstreetmap.josm.tools.I18n.i18n; 
    77import static org.openstreetmap.josm.tools.I18n.tr; 
    8  
    98 
    109import java.awt.EventQueue; 
     
    2423 
    2524import javax.swing.JFrame; 
    26 import javax.swing.JOptionPane; 
    2725 
    2826import org.openstreetmap.josm.Main; 
    29 import org.openstreetmap.josm.plugins.PluginDownloader; 
     27import org.openstreetmap.josm.plugins.PluginHandler; 
    3028import org.openstreetmap.josm.tools.BugReportExceptionHandler; 
    3129import org.openstreetmap.josm.tools.ImageProvider; 
     
    6765     */ 
    6866    public static void main(final String[] argArray) { 
    69         ///////////////////////////////////////////////////////////////////////// 
    70         //                        TO ALL TRANSLATORS 
    71         ///////////////////////////////////////////////////////////////////////// 
    72         // Do not translate the early strings below until the locale is set up. 
    73         // (By the eager loaded plugins) 
    74         // 
    75         // These strings cannot be translated. That's life. Really. Sorry. 
    76         // 
    77         //                                                                 Imi. 
    78         ///////////////////////////////////////////////////////////////////////// 
     67        /* try initial language settings, may be changed later again */ 
     68        try { i18n = I18nFactory.getI18n(MainApplication.class); } 
     69        catch (MissingResourceException ex) { Locale.setDefault(Locale.ENGLISH);} 
    7970 
    8071        Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler()); 
     
    10192        } 
    10293 
     94        Main.pref.init(args.containsKey("reset-preferences")); 
     95 
     96        String localeName = null; // The locale to use 
     97 
     98        //Check if passed as parameter 
     99        if(args.containsKey("language")) 
     100            localeName = (String)(args.get("language").toArray()[0]); 
     101 
     102        if (localeName == null) 
     103            localeName = Main.pref.get("language", null); 
     104 
     105        if (localeName != null) { 
     106            Locale l; 
     107            Locale d = Locale.getDefault(); 
     108            if(localeName.equals("he")) localeName = "iw_IL"; 
     109            int i = localeName.indexOf('_'); 
     110            if (i > 0) { 
     111                l = new Locale(localeName.substring(0, i), localeName.substring(i + 1)); 
     112            } else { 
     113                l = new Locale(localeName); 
     114            } 
     115            try { 
     116                Locale.setDefault(l); 
     117                i18n = I18nFactory.getI18n(MainApplication.class); 
     118            } catch (MissingResourceException ex) { 
     119                if(!l.getLanguage().equals("en")) 
     120                { 
     121                    System.out.println(tr("Unable to find translation for the locale {0}. Reverting to {1}.", 
     122                    l.getDisplayName(), d.getDisplayName())); 
     123                    Locale.setDefault(d); 
     124                } 
     125            } 
     126        } 
     127 
    103128        if (argList.contains("--help") || argList.contains("-?") || argList.contains("-h")) { 
    104129            // TODO: put in a platformHook for system that have no console by default 
     
    122147                    "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+ 
    123148                    "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n\n"+ 
    124  
    125149                    tr("Parameters are read in the order they are specified, so make sure you load\n"+ 
    126150                    "some data before --selection")+"\n\n"+ 
     
    129153        } 
    130154 
    131         // get the preferences. 
    132         final File prefDir = new File(Main.pref.getPreferencesDir()); 
    133         if (prefDir.exists()) { 
    134             if(!prefDir.isDirectory()) { 
    135                 JOptionPane.showMessageDialog(null, tr("Cannot open preferences directory: {0}",Main.pref.getPreferencesDir())); 
    136                 return; 
    137             } 
    138         } 
    139         else 
    140             prefDir.mkdirs(); 
    141  
    142         if (!new File(Main.pref.getPreferencesDir()+"preferences").exists()) { 
    143             Main.pref.resetToDefault(); 
    144         } 
    145  
    146         try { 
    147             if (args.containsKey("reset-preferences")) { 
    148                 Main.pref.resetToDefault(); 
    149             } else { 
    150                 Main.pref.load(); 
    151             } 
    152         } catch (final IOException e1) { 
    153             e1.printStackTrace(); 
    154             String backup = Main.pref.getPreferencesDir() + "preferences.bak"; 
    155             JOptionPane.showMessageDialog(null, tr("Preferences file had errors. Making backup of old one to {0}.", backup)); 
    156             new File(Main.pref.getPreferencesDir() + "preferences").renameTo(new File(backup)); 
    157             Main.pref.save(); 
    158         } 
    159  
    160         String localeName = null; //The locale to use 
    161  
    162         //Check if passed as parameter 
    163         if(args.containsKey("language")) 
    164             localeName = (String)(args.get("language").toArray()[0]); 
    165  
    166         if (localeName == null) { 
    167             localeName = Main.pref.get("language", null); 
    168         } 
    169  
    170         if (localeName != null) { 
    171             if(localeName.equals("he")) localeName = "iw_IL"; 
    172             Locale l; 
    173             int i = localeName.indexOf('_'); 
    174             if (i > 0) { 
    175                 l = new Locale(localeName.substring(0, i), localeName.substring(i + 1)); 
    176             } else { 
    177                 l = new Locale(localeName); 
    178             } 
    179             Locale.setDefault(l); 
    180         } 
    181         try { 
    182             i18n = I18nFactory.getI18n(MainApplication.class); 
    183         } catch (MissingResourceException ex) { 
    184             if(!Locale.getDefault().getLanguage().equals("en")) 
    185             { 
    186                 System.out.println("Unable to find translation for the locale: " 
    187                 + Locale.getDefault().getDisplayName() + " reverting to English."); 
    188                 Locale.setDefault(Locale.ENGLISH); 
    189             } 
    190         } 
    191  
    192155        SplashScreen splash = new SplashScreen(Main.pref.getBoolean("draw.splashscreen", true)); 
    193156 
    194157        splash.setStatus(tr("Activating updated plugins")); 
    195         if (!PluginDownloader.moveUpdatedPlugins()) { 
    196             JOptionPane.showMessageDialog(null, 
    197                     tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."), 
    198                     tr("Plugins"), JOptionPane.ERROR_MESSAGE); 
    199         } 
     158        PluginHandler.earlyCleanup(); 
    200159 
    201         // load the early plugins 
    202160        splash.setStatus(tr("Loading early plugins")); 
    203         Main.loadPlugins(true); 
     161        PluginHandler.loadPlugins(true); 
    204162 
    205163        splash.setStatus(tr("Setting defaults")); 
     
    210168        final Main main = new MainApplication(mainFrame, splash); 
    211169        splash.setStatus(tr("Loading plugins")); 
    212         Main.loadPlugins(false); 
     170        PluginHandler.loadPlugins(false); 
    213171        toolbar.refreshToolbarControl(); 
    214172 
  • trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java

    r1307 r1326  
    3030import org.openstreetmap.josm.data.Bounds; 
    3131import org.openstreetmap.josm.gui.MapView; 
    32 import org.openstreetmap.josm.plugins.PluginProxy; 
     32import org.openstreetmap.josm.plugins.PluginHandler; 
    3333import org.openstreetmap.josm.tools.GBC; 
    3434import org.openstreetmap.josm.tools.OsmUrlToBounds; 
     
    107107 
    108108        // add selections from plugins 
    109         for (PluginProxy p : Main.plugins) { 
    110             p.addDownloadSelection(downloadSelections); 
    111         } 
     109        PluginHandler.addDownloadSelection(downloadSelections); 
    112110 
    113111        // now everybody may add their tab to the tabbed pane 
  • trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java

    r1212 r1326  
    33 
    44import static org.openstreetmap.josm.tools.I18n.tr; 
    5 import static org.openstreetmap.josm.tools.I18n.trn; 
    65 
    76import java.awt.Dimension; 
    8 import java.awt.GridBagConstraints; 
    97import java.awt.GridBagLayout; 
    10 import java.awt.Insets; 
    118import java.awt.Rectangle; 
    129import java.awt.event.ActionEvent; 
    1310import java.awt.event.ActionListener; 
    14 import java.io.File; 
    15 import java.io.FileReader; 
    16 import java.io.IOException; 
    17 import java.util.Arrays; 
    1811import java.util.Collection; 
    19 import java.util.Collections; 
    20 import java.util.Comparator; 
    21 import java.util.HashMap; 
    22 import java.util.HashSet; 
    2312import java.util.LinkedList; 
    24 import java.util.Map; 
    25 import java.util.Set; 
    26 import java.util.SortedMap; 
    27 import java.util.TreeMap; 
    28 import java.util.Map.Entry; 
    2913 
    3014import javax.swing.AbstractAction; 
    31 import javax.swing.BorderFactory; 
    3215import javax.swing.DefaultListModel; 
    3316import javax.swing.JButton; 
    34 import javax.swing.JCheckBox; 
    35 import javax.swing.JEditorPane; 
    3617import javax.swing.JLabel; 
    3718import javax.swing.JList; 
     
    4021import javax.swing.JScrollPane; 
    4122import javax.swing.Scrollable; 
    42 import javax.swing.UIManager; 
    43 import javax.swing.event.HyperlinkEvent; 
    44 import javax.swing.event.HyperlinkListener; 
    45 import javax.swing.event.HyperlinkEvent.EventType; 
    4623 
    4724import org.openstreetmap.josm.Main; 
    4825import org.openstreetmap.josm.plugins.PluginDownloader; 
    49 import org.openstreetmap.josm.plugins.PluginException; 
    50 import org.openstreetmap.josm.plugins.PluginInformation; 
    51 import org.openstreetmap.josm.plugins.PluginProxy; 
     26import org.openstreetmap.josm.plugins.PluginSelection; 
    5227import org.openstreetmap.josm.tools.GBC; 
    53 import org.openstreetmap.josm.tools.OpenBrowser; 
    54 import org.openstreetmap.josm.tools.XmlObjectParser.Uniform; 
    5528 
    5629public class PluginPreference implements PreferenceSetting { 
    5730 
    58     /** 
    59      * Only the plugin name, its jar location and the description. 
    60      * In other words, this is the minimal requirement the plugin preference page 
    61      * needs to show the plugin as available 
    62      * 
    63      * @author imi 
    64      */ 
    65     public static class PluginDescription implements Comparable<Object> { 
    66         // Note: All the following need to be public instance variables of 
    67         // type String.  (Plugin description XMLs from the server are parsed 
    68         // with tools.XmlObjectParser, which uses reflection to access them.) 
    69         public String name; 
    70         public String description; 
    71         public String resource; 
    72         public String version; 
    73         public PluginDescription(String name, String description, String resource, String version) { 
    74             this.name = name; 
    75             this.description = description; 
    76             this.resource = resource; 
    77             this.version = version; 
    78         } 
    79         public PluginDescription() { 
    80         } 
    81         public int compareTo(Object n) { 
    82             if(n instanceof PluginDescription) 
    83                 return name.compareToIgnoreCase(((PluginDescription)n).name); 
    84             return -1; 
    85         } 
    86     } 
    87  
    88     private Map<String, Boolean> pluginMap; 
    89     private Map<String, PluginDescription> availablePlugins; 
    9031    private JPanel plugin; 
    9132    private JPanel pluginPanel = new NoHorizontalScrollPanel(new GridBagLayout()); 
    9233    private PreferenceDialog gui; 
    9334    private JScrollPane pluginPane; 
     35    private PluginSelection selection = new PluginSelection(); 
    9436 
    9537    public void addGui(final PreferenceDialog gui) { 
     
    10345        morePlugins.addActionListener(new ActionListener(){ 
    10446            public void actionPerformed(ActionEvent e) { 
    105                 int count = PluginDownloader.downloadDescription(); 
    106                 if (count > 0) 
    107                     JOptionPane.showMessageDialog(Main.parent, 
    108                         trn("Downloaded plugin information from {0} site", 
    109                             "Downloaded plugin information from {0} sites", count, count)); 
    110                 else 
    111                     JOptionPane.showMessageDialog(Main.parent, tr("No plugin information found.")); 
    112                 refreshPluginPanel(gui); 
     47                selection.updateDescription(pluginPanel); 
    11348            } 
    11449        }); 
     
    11853        update.addActionListener(new ActionListener(){ 
    11954            public void actionPerformed(ActionEvent e) { 
    120                 update(); 
    121                 refreshPluginPanel(gui); 
     55                selection.update(pluginPanel); 
    12256            } 
    12357        }); 
     
    13266        plugin.add(configureSites, GBC.std()); 
    13367 
    134         refreshPluginPanel(gui); 
     68        selection.drawPanel(pluginPanel); 
    13569    } 
    13670 
     
    174108        if (answer != JOptionPane.OK_OPTION) 
    175109            return; 
    176         StringBuilder b = new StringBuilder(); 
    177         for (int i = 0; i < model.getSize(); ++i) { 
    178             b.append(model.getElementAt(i)); 
    179             if (i < model.getSize()-1) 
    180                 b.append(" "); 
    181         } 
    182         Main.pref.put("pluginmanager.sites", b.toString()); 
    183     } 
    184  
    185     private void update() { 
    186         // refresh description 
    187         int num = PluginDownloader.downloadDescription(); 
    188         Boolean done = false; 
    189         refreshPluginPanel(gui); 
    190  
    191         Set<PluginDescription> toUpdate = new HashSet<PluginDescription>(); 
    192         StringBuilder toUpdateStr = new StringBuilder(); 
    193         for (PluginProxy proxy : Main.plugins) { 
    194             PluginDescription description = availablePlugins.get(proxy.info.name); 
    195             if (description != null && (description.version == null || description.version.equals("")) ? 
    196             (proxy.info.version != null && proxy.info.version.equals("")) : !description.version.equals(proxy.info.version)) { 
    197                 toUpdate.add(description); 
    198                 toUpdateStr.append(description.name+"\n"); 
    199             } 
    200         } 
    201         if (toUpdate.isEmpty()) { 
    202             JOptionPane.showMessageDialog(Main.parent, tr("All installed plugins are up to date.")); 
    203             done = true; 
    204         } else { 
    205             int answer = JOptionPane.showConfirmDialog(Main.parent, tr("Update the following plugins:\n\n{0}", 
    206             toUpdateStr.toString()), tr("Update"), JOptionPane.OK_CANCEL_OPTION); 
    207             if (answer == JOptionPane.OK_OPTION) { 
    208                 PluginDownloader.update(toUpdate); 
    209                 done = true; 
    210             } 
    211         } 
    212         if (done && num >= 1) 
    213             Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis())); 
    214     } 
    215  
    216     private void refreshPluginPanel(final PreferenceDialog gui) { 
    217         availablePlugins = getAvailablePlugins(); 
    218         Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null); 
    219  
    220         if (pluginMap == null) 
    221             pluginMap = new HashMap<String, Boolean>(); 
    222         else 
    223             // Keep the map in bounds; possibly slightly pointless. 
    224             for (final String pname : pluginMap.keySet()) 
    225                 if (availablePlugins.get(pname) == null) pluginMap.remove(pname); 
    226  
    227         pluginPanel.removeAll(); 
    228  
    229         GridBagConstraints gbc = new GridBagConstraints(); 
    230         gbc.gridx = 0; 
    231         gbc.anchor = GridBagConstraints.NORTHWEST; 
    232  
    233         int row = 0; 
    234         for (final PluginDescription plugin : availablePlugins.values()) { 
    235             boolean enabled = (enabledPlugins != null) && enabledPlugins.contains(plugin.name); 
    236             if (pluginMap.get(plugin.name) == null) 
    237                 pluginMap.put(plugin.name, enabled); 
    238  
    239             String remoteversion = plugin.version; 
    240             if ((remoteversion == null) || remoteversion.equals("")) 
    241                 remoteversion = tr("unknown"); 
    242  
    243             String localversion; 
    244             PluginInformation p = PluginInformation.findPlugin(plugin.name); 
    245             if (p != null) { 
    246                 if (p.version != null && !p.version.equals("")) 
    247                     localversion = p.version; 
    248                 else 
    249                     localversion = tr("unknown"); 
    250                 localversion = " (" + localversion + ")"; 
    251             } else 
    252                 localversion = ""; 
    253  
    254             final JCheckBox pluginCheck = new JCheckBox( 
    255                     tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion), 
    256                     pluginMap.get(plugin.name)); 
    257             gbc.gridy = row++; 
    258             gbc.insets = new Insets(5,5,0,5); 
    259             gbc.weighty = 0.1; 
    260             gbc.fill = GridBagConstraints.NONE; 
    261             pluginPanel.add(pluginCheck, gbc); 
    262  
    263             pluginCheck.setToolTipText(plugin.resource != null ? ""+plugin.resource : tr("Plugin bundled with JOSM")); 
    264  
    265             JEditorPane description = new JEditorPane(); 
    266             description.setContentType("text/html"); 
    267             description.setEditable(false); 
    268             description.setText("<html><i>"+(plugin.description==null?tr("no description available"):plugin.description)+"</i></html>"); 
    269             description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0)); 
    270             description.setBackground(UIManager.getColor("Panel.background")); 
    271             description.addHyperlinkListener(new HyperlinkListener() { 
    272                 public void hyperlinkUpdate(HyperlinkEvent e) { 
    273                     if(e.getEventType() == EventType.ACTIVATED) { 
    274                         OpenBrowser.displayUrl(e.getURL().toString()); 
    275                     } 
    276                 } 
    277             }); 
    278  
    279             gbc.gridy = row++; 
    280             gbc.insets = new Insets(3,5,5,5); 
    281             gbc.weighty = 0.9; 
    282             gbc.weightx = 1.0; 
    283             gbc.anchor = GridBagConstraints.WEST; 
    284             gbc.fill = GridBagConstraints.HORIZONTAL; 
    285             pluginPanel.add(description, gbc); 
    286  
    287             pluginCheck.addActionListener(new ActionListener(){ 
    288                 public void actionPerformed(ActionEvent e) { 
    289                     // if user enabled a plugin, it is not loaded but found somewhere on disk: offer to delete jar 
    290                     if (pluginCheck.isSelected()) { 
    291                         PluginInformation plinfo = PluginInformation.findPlugin(plugin.name); 
    292                         if ((PluginInformation.getLoaded(plugin.name) == null) && (plinfo != null)) { 
    293                             try { 
    294                                 int answer = JOptionPane.showConfirmDialog(Main.parent, 
    295                                     tr("Plugin archive already available. Do you want to download the current version by deleting existing archive?\n\n{0}", 
    296                                     plinfo.file.getCanonicalPath()), tr("Plugin already exists"), JOptionPane.OK_CANCEL_OPTION); 
    297                                 if (answer == JOptionPane.OK_OPTION) { 
    298                                     if (!plinfo.file.delete()) { 
    299                                         JOptionPane.showMessageDialog(Main.parent, tr("Error deleting plugin file: {0}", plinfo.file.getCanonicalPath())); 
    300                                     } 
    301                                 } 
    302                             } catch (IOException e1) { 
    303                                 e1.printStackTrace(); 
    304                                 JOptionPane.showMessageDialog(Main.parent, tr("Error deleting plugin file: {0}", e1.getMessage())); 
    305                             } 
    306                         } 
    307                     } 
    308                     pluginMap.put(plugin.name, pluginCheck.isSelected()); 
    309                 } 
    310             }); 
    311         } 
    312         plugin.updateUI(); 
    313     } 
    314  
    315     private Map<String, PluginDescription> getAvailablePlugins() { 
    316         SortedMap<String, PluginDescription> availablePlugins = new TreeMap<String, PluginDescription>(new Comparator<String>(){ 
    317             public int compare(String o1, String o2) { 
    318                 return o1.compareToIgnoreCase(o2); 
    319             } 
    320         }); 
    321         for (String location : PluginInformation.getPluginLocations()) { 
    322             File[] pluginFiles = new File(location).listFiles(); 
    323             if (pluginFiles != null) { 
    324                 Arrays.sort(pluginFiles); 
    325                 for (File f : pluginFiles) { 
    326                     if (!f.isFile()) 
    327                         continue; 
    328                     if (f.getName().endsWith(".jar")) { 
    329                         try { 
    330                             PluginInformation info = new PluginInformation(f); 
    331                             if (!availablePlugins.containsKey(info.name)) 
    332                                 availablePlugins.put(info.name, new PluginDescription( 
    333                                     info.name, 
    334                                     info.description, 
    335                                     PluginInformation.fileToURL(f).toString(), 
    336                                     info.version)); 
    337                         } catch (PluginException x) { 
    338                         } 
    339                     } else if (f.getName().matches("^[0-9]+-site.*\\.xml$")) { 
    340                         try { 
    341                             Uniform<PluginDescription> parser = new Uniform<PluginDescription>(new FileReader(f), "plugin", PluginDescription.class); 
    342                             for (PluginDescription pd : parser) 
    343                                 if (!availablePlugins.containsKey(pd.name)) 
    344                                     availablePlugins.put(pd.name, pd); 
    345                         } catch (Exception e) { 
    346                             e.printStackTrace(); 
    347                             JOptionPane.showMessageDialog(Main.parent, tr("Error reading plugin information file: {0}", f.getName())); 
    348                         } 
    349                     } 
    350                 } 
    351             } 
    352         } 
    353         for (PluginProxy proxy : Main.plugins) 
    354             if (!availablePlugins.containsKey(proxy.info.name)) 
    355                 availablePlugins.put(proxy.info.name, new PluginDescription( 
    356                         proxy.info.name, 
    357                         proxy.info.description, 
    358                         proxy.info.file == null ? null : 
    359                             PluginInformation.fileToURL(proxy.info.file).toString(), 
    360                         proxy.info.version)); 
    361         return availablePlugins; 
     110        Collection<String> sites = new LinkedList<String>(); 
     111        for (int i = 0; i < model.getSize(); ++i) 
     112            sites.add((String)model.getElementAt(i)); 
     113        PluginDownloader.setSites(sites); 
    362114    } 
    363115 
    364116    public boolean ok() { 
    365         Collection<PluginDescription> toDownload = new LinkedList<PluginDescription>(); 
    366         String msg = ""; 
    367         for (Entry<String, Boolean> entry : pluginMap.entrySet()) { 
    368             if (entry.getValue() && PluginInformation.findPlugin(entry.getKey()) == null) { 
    369                 toDownload.add(availablePlugins.get(entry.getKey())); 
    370                 msg += entry.getKey() + "\n"; 
    371             } 
    372         } 
    373         if (!toDownload.isEmpty()) { 
    374             int answer = JOptionPane.showConfirmDialog(Main.parent, 
    375                     tr("Download the following plugins?\n\n{0}", msg), 
    376                     tr("Download missing plugins"), 
    377                     JOptionPane.YES_NO_OPTION); 
    378             if (answer != JOptionPane.OK_OPTION) 
    379                 for (PluginDescription pd : toDownload) 
    380                     pluginMap.put(pd.name, false); 
    381             else 
    382                 for (PluginDescription pd : toDownload) 
    383                     if (!PluginDownloader.downloadPlugin(pd)) 
    384                         pluginMap.put(pd.name, false); 
     117        return selection.finish(); 
     118    } 
    385119 
    386         } 
    387         LinkedList<String> plugins = new LinkedList<String>(); 
    388         for (Map.Entry<String, Boolean> d : pluginMap.entrySet()) { 
    389             if (d.getValue()) 
    390                 plugins.add(d.getKey()); 
    391         } 
    392  
    393         Collections.sort(plugins); 
    394         return Main.pref.putCollection("plugins", plugins); 
    395     } 
    396      
    397120    class NoHorizontalScrollPanel extends JPanel implements Scrollable { 
    398121        public NoHorizontalScrollPanel(GridBagLayout gridBagLayout) { 
  • trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java

    r1180 r1326  
    2222 
    2323import org.openstreetmap.josm.Main; 
    24 import org.openstreetmap.josm.plugins.PluginProxy; 
     24import org.openstreetmap.josm.plugins.PluginHandler; 
     25import org.openstreetmap.josm.tools.BugReportExceptionHandler; 
    2526import org.openstreetmap.josm.tools.GBC; 
    2627import org.openstreetmap.josm.tools.I18n; 
     
    112113            } catch (SecurityException e) { 
    113114                it.remove(); 
     115            } catch (Throwable e) { 
     116                /* allow to change most settings even if e.g. a plugin fails */ 
     117                BugReportExceptionHandler.handleException(e); 
    114118            } 
    115119        } 
     
    133137        settings.add(new ShortcutPreference()); 
    134138 
    135         for (PluginProxy plugin : Main.plugins) { 
    136             PreferenceSetting p = plugin.getPreferenceSetting(); 
    137             if (p != null) 
    138                 settings.add(p); 
    139         } 
     139        PluginHandler.getPreferenceSetting(settings); 
    140140 
    141141        // always the last: advanced tab 
  • trunk/src/org/openstreetmap/josm/plugins/PluginDownloader.java

    r1212 r1326  
    2929import org.openstreetmap.josm.Main; 
    3030import org.openstreetmap.josm.gui.PleaseWaitRunnable; 
    31 import org.openstreetmap.josm.gui.preferences.PluginPreference.PluginDescription; 
    3231import org.xml.sax.SAXException; 
    3332 
     
    7675    public static Collection<String> getSites() { 
    7776        return Main.pref.getCollection("pluginmanager.sites", Arrays.asList(pluginSites)); 
     77    } 
     78    public static void setSites(Collection<String> c) { 
     79        Main.pref.putCollection("pluginmanager.sites", c); 
    7880    } 
    7981 
  • trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java

    r1169 r1326  
    3434    public final String mainversion; 
    3535    public final String className; 
     36    public final String requires; 
    3637    public final String description; 
    3738    public final boolean early; 
     
    7778                Attributes attr = manifest.getMainAttributes(); 
    7879                className = attr.getValue("Plugin-Class"); 
     80                requires = attr.getValue("Plugin-Requires"); 
    7981                description = attr.getValue("Plugin-Description"); 
    8082                early = Boolean.parseBoolean(attr.getValue("Plugin-Early")); 
     
    107109                early = false; 
    108110                stage = 50; 
     111                requires = null; 
    109112                version = null; 
    110113                author = null; 
     
    215218        return all; 
    216219    } 
    217  
    218  
    219     /** 
    220      * Return information about a loaded plugin. 
    221      * 
    222      * Note that if you call this in your plugins bootstrap, you may get <code>null</code> if 
    223      * the plugin requested is not loaded yet. 
    224      * 
    225      * @return The PluginInformation to a specific plugin, but only if the plugin is loaded. 
    226      * If it is not loaded, <code>null</code> is returned. 
    227      */ 
    228     public static PluginInformation getLoaded(String pluginName) { 
    229         for (PluginProxy p : Main.plugins) 
    230             if (p.info.name.equals(pluginName)) 
    231                 return p.info; 
    232         return null; 
    233     } 
    234220} 
    235221 
  • trunk/src/org/openstreetmap/josm/plugins/PluginProxy.java

    r1169 r1326  
    77import org.openstreetmap.josm.gui.download.DownloadSelection; 
    88import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 
    9  
     9import org.openstreetmap.josm.tools.BugReportExceptionHandler; 
    1010 
    1111/** 
     
    3131        } catch (NoSuchMethodException e) { 
    3232        } catch (Exception e) { 
    33             throw new PluginException(this, info.name, e); 
     33            BugReportExceptionHandler.handleException(new PluginException(this, info.name, e)); 
    3434        } 
    3535    } 
     
    4141            return null; 
    4242        } catch (Exception e) { 
    43             throw new PluginException(this, info.name, e); 
     43            BugReportExceptionHandler.handleException(new PluginException(this, info.name, e)); 
    4444        } 
     45        return null; 
    4546    } 
    4647 
     
    5152            // ignore 
    5253        } catch (Exception e) { 
    53             throw new PluginException(this, info.name, e); 
     54            BugReportExceptionHandler.handleException(new PluginException(this, info.name, e)); 
    5455        } 
    5556    } 
  • trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java

    r1271 r1326  
    3030import org.openstreetmap.josm.Main; 
    3131import org.openstreetmap.josm.actions.AboutAction; 
    32 import org.openstreetmap.josm.plugins.PluginException; 
    33 import org.openstreetmap.josm.plugins.PluginProxy; 
     32import org.openstreetmap.josm.plugins.PluginHandler; 
    3433 
    3534/** 
     
    4140 
    4241    public void uncaughtException(Thread t, Throwable e) { 
     42        handleException(e); 
     43    } 
     44    public static void handleException(Throwable e) { 
    4345        e.printStackTrace(); 
    4446        if (Main.parent != null) { 
     
    5254            } 
    5355 
    54             PluginProxy plugin = null; 
    55  
    56             // Check for an explicit problem when calling a plugin function 
    57             if (e instanceof PluginException) 
    58                 plugin = ((PluginException)e).plugin; 
    59  
    60             if (plugin == null) 
    61                 plugin = guessPlugin(e); 
    62  
    63             if (plugin != null) { 
    64                 int answer = JOptionPane.showConfirmDialog( 
    65                         Main.parent, tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.", 
    66                         plugin.info.name) + "\n"+ (plugin.info.author != null ? 
    67                         tr("According to the information within the plugin, the author is {0}.", 
    68                         plugin.info.author) : "") + "\n" + 
    69                         tr("Try updating to the newest version of this plugin before reporting a bug.") + "\n" + 
    70                         tr("Should the plugin be disabled?"), 
    71                         tr("Disable plugin"), 
    72                         JOptionPane.YES_NO_OPTION); 
    73                 if (answer == JOptionPane.OK_OPTION) { 
    74                     LinkedList<String> plugins = new LinkedList<String>(Arrays.asList(Main.pref.get("plugins").split(","))); 
    75                     if (plugins.contains(plugin.info.name)) { 
    76                         while (plugins.remove(plugin.info.name)) {} 
    77                         String p = ""; 
    78                         for (String s : plugins) 
    79                             p += ","+s; 
    80                         if (p.length() > 0) 
    81                             p = p.substring(1); 
    82                         Main.pref.put("plugins", p); 
    83                         JOptionPane.showMessageDialog(Main.parent, 
    84                         tr("The plugin has been removed from the configuration. Please restart JOSM to unload the plugin.")); 
    85                     } else { 
    86                         JOptionPane.showMessageDialog(Main.parent, 
    87                         tr("The plugin could not be removed. Please tell the people you got JOSM from about the problem.")); 
    88                     } 
    89                     return; 
    90                 } 
    91             } 
     56            if(PluginHandler.checkException(e)) 
     57                return; 
    9258 
    9359            Object[] options = new String[]{tr("Do nothing"), tr("Report Bug")}; 
     
    10369 
    10470                    String text = AboutAction.getTextBlock(); 
    105                     String pl = Main.pref.get("plugins"); 
    10671                    text += "Java version: " + System.getProperty("java.version")+"\n"; 
    107                     if(pl != null && pl.length() != 0) 
    108                         text += "Plugins: "+pl+"\n"; 
    109                     for (final PluginProxy pp : Main.plugins) { 
    110                         text += "Plugin " + pp.info.name + (pp.info.version != null && !pp.info.version.equals("") ? " Version: "+pp.info.version+"\n" : "\n"); 
    111                     } 
     72                    text += PluginHandler.getBugReportText(); 
    11273                    text += "\n" + stack.getBuffer().toString(); 
    11374 
     
    13798        } 
    13899    } 
    139  
    140     private PluginProxy guessPlugin(Throwable e) { 
    141         String name = guessPluginName(e); 
    142         for (PluginProxy p : Main.plugins) 
    143             if (p.info.name.equals(name)) 
    144                 return p; 
    145         return null; 
    146     } 
    147  
    148     /** 
    149      * Analyze the stack of the argument and return a name of a plugin, if 
    150      * some known problem pattern has been found or <code>null</code>, if 
    151      * the stack does not contain plugin-code. 
    152      * 
    153      * Note: This heuristic is not meant as discrimination against specific 
    154      * plugins, but only to stop the flood of similar bug reports about plugins. 
    155      * Of course, plugin writers are free to install their own version of 
    156      * an exception handler with their email address listed to receive 
    157      * bug reports ;-). 
    158      */ 
    159     private String guessPluginName(Throwable e) { 
    160         for (StackTraceElement element : e.getStackTrace()) { 
    161             String c = element.getClassName(); 
    162  
    163             if (c.contains("wmsplugin.") || c.contains(".WMSLayer")) 
    164                 return "wmsplugin"; 
    165             if (c.contains("livegps.")) 
    166                 return "livegps"; 
    167             if (c.startsWith("UtilsPlugin.")) 
    168                 return "UtilsPlugin"; 
    169  
    170             if (c.startsWith("org.openstreetmap.josm.plugins.")) { 
    171                 String p = c.substring("org.openstreetmap.josm.plugins.".length()); 
    172                 if (p.indexOf('.') != -1 && p.matches("[a-z].*")) { 
    173                     return p.substring(0,p.indexOf('.')); 
    174                 } 
    175             } 
    176         } 
    177         return null; 
    178     } 
    179100} 
Note: See TracChangeset for help on using the changeset viewer.