source: josm/trunk/src/org/openstreetmap/josm/plugins/PluginSelection.java@ 2198

Last change on this file since 2198 was 2152, checked in by Gubaer, 15 years ago

applied #3524: patch by avar: Make plugin search (much) faster

File size: 17.5 KB
Line 
1//License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.plugins;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.GridBagConstraints;
8import java.awt.Insets;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.io.BufferedReader;
12import java.io.ByteArrayInputStream;
13import java.io.File;
14import java.io.FileInputStream;
15import java.io.InputStreamReader;
16import java.util.Arrays;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.Comparator;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.LinkedList;
23import java.util.Map;
24import java.util.Set;
25import java.util.TreeMap;
26import java.util.Map.Entry;
27
28import javax.swing.BorderFactory;
29import javax.swing.JCheckBox;
30import javax.swing.JEditorPane;
31import javax.swing.JOptionPane;
32import javax.swing.JPanel;
33import javax.swing.JTextField;
34import javax.swing.UIManager;
35import javax.swing.event.HyperlinkEvent;
36import javax.swing.event.HyperlinkListener;
37import javax.swing.event.HyperlinkEvent.EventType;
38
39import org.openstreetmap.josm.Main;
40import org.openstreetmap.josm.gui.ExtendedDialog;
41import org.openstreetmap.josm.tools.OpenBrowser;
42
43public class PluginSelection {
44
45 private Map<String, Boolean> pluginMap;
46 private Map<String, PluginInformation> availablePlugins;
47 private Map<String, PluginInformation> localPlugins;
48
49 private JTextField txtFilter = null;
50
51 /* Get a copy of PluginPreference's txtField so we can use it for searching */
52 public void passTxtFilter(JTextField filter) {
53 txtFilter = filter;
54 }
55
56 public void updateDescription(JPanel pluginPanel) {
57 int count = PluginDownloader.downloadDescription();
58 if (count > 0) {
59 JOptionPane.showMessageDialog(Main.parent,
60 trn("Downloaded plugin information from {0} site",
61 "Downloaded plugin information from {0} sites", count, count),
62 tr("Information"),
63 JOptionPane.INFORMATION_MESSAGE
64 );
65 } else {
66 JOptionPane.showMessageDialog(
67 Main.parent,
68 tr("No plugin information found."),
69 tr("Error"),
70 JOptionPane.ERROR_MESSAGE
71 );
72 }
73 loadPlugins();
74 drawPanel(pluginPanel);
75 }
76
77 public void update(JPanel pluginPanel) {
78 // refresh description
79 int num = PluginDownloader.downloadDescription();
80 Boolean done = false;
81 loadPlugins();
82 drawPanel(pluginPanel);
83
84 Set<PluginInformation> toUpdate = new HashSet<PluginInformation>();
85 StringBuilder toUpdateStr = new StringBuilder();
86 for (String pluginName : Main.pref.getCollection("plugins", Collections.<String>emptySet())) {
87 PluginInformation local = localPlugins.get(pluginName);
88 PluginInformation description = availablePlugins.get(pluginName);
89
90 if (description == null) {
91 System.out.println(tr("Plug-in named {0} is not available. Update skipped.", pluginName));
92 continue;
93 }
94
95 if (local == null || (description.version != null && !description.version.equals(local.version))) {
96 toUpdate.add(description);
97 toUpdateStr.append(pluginName+"\n");
98 }
99 }
100 if (toUpdate.isEmpty()) {
101 JOptionPane.showMessageDialog(
102 Main.parent,
103 tr("All installed plugins are up to date."),
104 tr("Information"),
105 JOptionPane.INFORMATION_MESSAGE
106 );
107 done = true;
108 } else {
109 ExtendedDialog ed = new ExtendedDialog(Main.parent,
110 tr("Update"),
111 new String[] {tr("Update Plugins"), tr("Cancel")});
112 ed.setButtonIcons(new String[] {"dialogs/refresh.png", "cancel.png"});
113 ed.setContent(tr("Update the following plugins:\n\n{0}", toUpdateStr.toString()));
114 ed.showDialog();
115
116 if (ed.getValue() == 1) {
117 PluginDownloader.update(toUpdate);
118 done = true;
119 }
120 }
121 if (done && num >= 1) {
122 Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis()));
123 }
124 loadPlugins();
125 drawPanel(pluginPanel);
126 }
127
128 public boolean finish() {
129 Collection<PluginInformation> toDownload = new LinkedList<PluginInformation>();
130 Collection<String> installedPlugins = Main.pref.getCollection("plugins", Collections.<String>emptySet());
131
132 String msg = "";
133 for (Entry<String, Boolean> entry : pluginMap.entrySet()) {
134 if(entry.getValue() && !installedPlugins.contains(entry.getKey()))
135 {
136 String name = entry.getKey();
137 PluginInformation ap = availablePlugins.get(name);
138 PluginInformation pi = localPlugins.get(name);
139 if(pi == null || (pi.version == null && ap.version != null)
140 || (pi.version != null && !pi.version.equals(ap.version)))
141 {
142 toDownload.add(ap);
143 msg += name + "\n";
144 }
145 }
146 }
147 if (!toDownload.isEmpty()) {
148 ExtendedDialog ed = new ExtendedDialog(Main.parent,
149 tr("Download missing plugins"),
150 new String[] {tr("Download Plugins"), tr("Cancel")});
151 ed.setButtonIcons(new String[] {"download.png", "cancel.png"});
152 ed.setContent(tr("Download the following plugins?\n\n{0}", msg));
153 ed.showDialog();
154
155 Collection<PluginInformation> error =
156 (ed.getValue() != 1 ? toDownload : new PluginDownloader().download(toDownload));
157 for (PluginInformation pd : error) {
158 pluginMap.put(pd.name, false);
159 }
160
161 }
162 LinkedList<String> plugins = new LinkedList<String>();
163 for (Map.Entry<String, Boolean> d : pluginMap.entrySet()) {
164 if (d.getValue()) {
165 plugins.add(d.getKey());
166 }
167 }
168
169 Collections.sort(plugins);
170 return Main.pref.putCollection("plugins", plugins);
171 }
172
173 /* return true when plugin list changed */
174 public void drawPanel(JPanel pluginPanel) {
175 Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null);
176
177 if (pluginMap == null) {
178 pluginMap = new HashMap<String, Boolean>();
179 } else {
180 // Keep the map in bounds; possibly slightly pointless.
181 Set<String> pluginsToRemove = new HashSet<String>();
182 for (final String pname : pluginMap.keySet()) {
183 if (availablePlugins.get(pname) == null) {
184 pluginsToRemove.add(pname);
185 }
186 }
187
188 for (String pname : pluginsToRemove) {
189 pluginMap.remove(pname);
190 }
191 }
192
193 pluginPanel.removeAll();
194
195 GridBagConstraints gbc = new GridBagConstraints();
196 gbc.gridx = 0;
197 gbc.anchor = GridBagConstraints.NORTHWEST;
198
199 int row = 0;
200 for (final PluginInformation plugin : availablePlugins.values()) {
201 boolean enabled = (enabledPlugins != null) && enabledPlugins.contains(plugin.name);
202 if (pluginMap.get(plugin.name) == null) {
203 pluginMap.put(plugin.name, enabled);
204 }
205
206 String remoteversion = plugin.version;
207 if ((remoteversion == null) || remoteversion.equals("")) {
208 remoteversion = tr("unknown");
209 } else if(plugin.oldmode) {
210 remoteversion += "*";
211 }
212
213 String localversion = "";
214 PluginInformation p = localPlugins.get(plugin.name);
215 if(p != null)
216 {
217 if (p.version != null && !p.version.equals("")) {
218 localversion = p.version;
219 } else {
220 localversion = tr("unknown");
221 }
222 localversion = " (" + localversion + ")";
223 }
224
225 /* If this plugin doesn't match our search parameters we don't want to display it */
226 if (txtFilter.getText() != null) {
227 boolean matches = filterNameAndDescription(txtFilter.getText(),plugin.name,
228 plugin.getLinkDescription(),
229 remoteversion, localversion);
230 if (!matches) {
231 /* This is not the plugin you're looking for */
232 continue;
233 }
234 }
235
236 final JCheckBox pluginCheck = new JCheckBox(
237 tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion),
238 pluginMap.get(plugin.name));
239 gbc.gridy = row++;
240 gbc.insets = new Insets(5,5,0,5);
241 gbc.weighty = 0.1;
242 gbc.fill = GridBagConstraints.NONE;
243 pluginPanel.add(pluginCheck, gbc);
244
245 pluginCheck.setToolTipText(plugin.downloadlink != null ? ""+plugin.downloadlink : tr("Plugin bundled with JOSM"));
246
247 JEditorPane description = new JEditorPane();
248 description.setContentType("text/html");
249 description.setEditable(false);
250 description.setText("<html><body bgcolor=\"#" + Integer.toHexString( UIManager.getColor("Panel.background").getRGB() & 0x00ffffff ) +"\"><i>"+plugin.getLinkDescription()+"</i></body></html>");
251 description.setBorder(BorderFactory.createEmptyBorder());
252 description.setBackground(UIManager.getColor("Panel.background"));
253 description.addHyperlinkListener(new HyperlinkListener() {
254 public void hyperlinkUpdate(HyperlinkEvent e) {
255 if(e.getEventType() == EventType.ACTIVATED) {
256 OpenBrowser.displayUrl(e.getURL().toString());
257 }
258 }
259 });
260
261 gbc.gridy = row++;
262 gbc.insets = new Insets(3,25,5,5);
263 gbc.weighty = 0.9;
264 gbc.weightx = 1.0;
265 gbc.anchor = GridBagConstraints.WEST;
266 gbc.fill = GridBagConstraints.HORIZONTAL;
267 pluginPanel.add(description, gbc);
268
269 pluginCheck.addActionListener(new ActionListener(){
270 public void actionPerformed(ActionEvent e) {
271 pluginMap.put(plugin.name, pluginCheck.isSelected());
272 }
273 });
274 }
275 pluginPanel.updateUI();
276 }
277
278 private static boolean filterNameAndDescription(String filter, final String name, final String description,
279 final String remoteversion, final String localversion) {
280 final String input[] = filter.split("\\s+");
281 /* We're doing case-insensitive matching */
282 final String name_lc = name.toLowerCase();
283 final String description_lc = description.toLowerCase();
284 final String remoteversion_lc = remoteversion.toLowerCase();
285 final String localversion_lc = localversion.toLowerCase();
286
287 boolean canHas = true;
288
289 /* Make 'foo bar' search for 'bar' or 'foo' in both name and description */
290 for (String bit : input) {
291 final String lc_bit = bit.toLowerCase();
292 if (!name_lc.contains(lc_bit) &&
293 !description_lc.contains(lc_bit) &&
294 !remoteversion_lc.contains(lc_bit) &&
295 !localversion_lc.contains(lc_bit)) {
296 canHas = false;
297 }
298 }
299
300 return canHas;
301 }
302
303 public void loadPlugins() {
304 availablePlugins = new TreeMap<String, PluginInformation>(new Comparator<String>(){
305 public int compare(String o1, String o2) {
306 return o1.compareToIgnoreCase(o2);
307 }
308 });
309 localPlugins = new TreeMap<String, PluginInformation>(new Comparator<String>(){
310 public int compare(String o1, String o2) {
311 return o1.compareToIgnoreCase(o2);
312 }
313 });
314 for (String location : PluginInformation.getPluginLocations()) {
315 File[] pluginFiles = new File(location).listFiles();
316 if (pluginFiles != null) {
317 Arrays.sort(pluginFiles);
318 for (File f : pluginFiles) {
319 if (!f.isFile()) {
320 continue;
321 }
322 String fname = f.getName();
323 if (fname.endsWith(".jar")) {
324 try {
325 PluginInformation info = new PluginInformation(f,fname.substring(0,fname.length()-4));
326 if (!availablePlugins.containsKey(info.name)) {
327 availablePlugins.put(info.name, info);
328 }
329 if (!localPlugins.containsKey(info.name)) {
330 localPlugins.put(info.name, info);
331 }
332 } catch (PluginException x) {
333 }
334 } else if (fname.endsWith(".jar.new")) {
335 try {
336 PluginInformation info = new PluginInformation(f,fname.substring(0,fname.length()-8));
337 availablePlugins.put(info.name, info);
338 localPlugins.put(info.name, info);
339 } catch (PluginException x) {
340 }
341 } else if (fname.matches("^[0-9]+-site.*\\.txt$")) {
342 int err = 0;
343 try {
344 BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f), "utf-8"));
345 String name = null;
346 String url = null;
347 String manifest = null;
348 for (String line = r.readLine(); line != null; line = r.readLine())
349 {
350 if(line.startsWith("\t"))
351 {
352 line = line.substring(1);
353 if(line.length() > 70)
354 {
355 manifest += line.substring(0,70)+"\n";
356 line = " " + line.substring(70);
357 }
358 manifest += line+"\n";
359 }
360 else
361 {
362 if(name != null)
363 {
364 try
365 {
366 PluginInformation info = new PluginInformation(
367 new ByteArrayInputStream(manifest.getBytes("utf-8")),
368 name.substring(0,name.length()-4), url);
369 if(!availablePlugins.containsKey(info.name)) {
370 availablePlugins.put(info.name, info);
371 }
372 }
373 catch (Exception e)
374 {
375 e.printStackTrace();
376 ++err;
377 }
378 }
379 String x[] = line.split(";");
380 name = x[0];
381 url = x[1];
382 manifest = null;
383 }
384 }
385 if(name != null)
386 {
387 PluginInformation info = new PluginInformation(
388 new ByteArrayInputStream(manifest.getBytes("utf-8")),
389 name.substring(0,name.length()-4), url);
390 if(!availablePlugins.containsKey(info.name)) {
391 availablePlugins.put(info.name, info);
392 }
393 }
394 r.close();
395 } catch (Exception e) {
396 e.printStackTrace();
397 ++err;
398 }
399 if(err > 0)
400 {
401 JOptionPane.showMessageDialog(
402 Main.parent,
403 tr("Error reading plugin information file: {0}", f.getName()),
404 tr("Error"),
405 JOptionPane.ERROR_MESSAGE
406 );
407 }
408 }
409 }
410 }
411 }
412 for (PluginProxy proxy : PluginHandler.pluginList)
413 {
414 if (!availablePlugins.containsKey(proxy.info.name)) {
415 availablePlugins.put(proxy.info.name, proxy.info);
416 }
417 if (!localPlugins.containsKey(proxy.info.name)) {
418 localPlugins.put(proxy.info.name, proxy.info);
419 }
420 }
421 }
422}
Note: See TracBrowser for help on using the repository browser.