001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.preferences.plugin; 003 004 import java.io.File; 005 import java.util.ArrayList; 006 import java.util.Collection; 007 import java.util.Collections; 008 import java.util.Comparator; 009 import java.util.HashMap; 010 import java.util.HashSet; 011 import java.util.LinkedList; 012 import java.util.List; 013 import java.util.Observable; 014 import java.util.Set; 015 import java.util.Map.Entry; 016 017 import org.openstreetmap.josm.Main; 018 import org.openstreetmap.josm.plugins.PluginException; 019 import org.openstreetmap.josm.plugins.PluginInformation; 020 021 public class PluginPreferencesModel extends Observable{ 022 private final ArrayList<PluginInformation> availablePlugins = new ArrayList<PluginInformation>(); 023 private final ArrayList<PluginInformation> displayedPlugins = new ArrayList<PluginInformation>(); 024 private final HashMap<PluginInformation, Boolean> selectedPluginsMap = new HashMap<PluginInformation, Boolean>(); 025 private Set<String> pendingDownloads = new HashSet<String>(); 026 private String filterExpression; 027 private Set<String> currentActivePlugins; 028 029 public PluginPreferencesModel() { 030 currentActivePlugins = new HashSet<String>(); 031 currentActivePlugins.addAll(Main.pref.getCollection("plugins", currentActivePlugins)); 032 } 033 034 public void filterDisplayedPlugins(String filter) { 035 if (filter == null) { 036 displayedPlugins.clear(); 037 displayedPlugins.addAll(availablePlugins); 038 this.filterExpression = null; 039 return; 040 } 041 displayedPlugins.clear(); 042 for (PluginInformation pi: availablePlugins) { 043 if (pi.matches(filter)) { 044 displayedPlugins.add(pi); 045 } 046 } 047 filterExpression = filter; 048 clearChanged(); 049 notifyObservers(); 050 } 051 052 public void setAvailablePlugins(Collection<PluginInformation> available) { 053 availablePlugins.clear(); 054 if (available != null) { 055 availablePlugins.addAll(available); 056 } 057 sort(); 058 filterDisplayedPlugins(filterExpression); 059 Set<String> activePlugins = new HashSet<String>(); 060 activePlugins.addAll(Main.pref.getCollection("plugins", activePlugins)); 061 for (PluginInformation pi: availablePlugins) { 062 if (selectedPluginsMap.get(pi) == null) { 063 if (activePlugins.contains(pi.name)) { 064 selectedPluginsMap.put(pi, true); 065 } 066 } 067 } 068 clearChanged(); 069 notifyObservers(); 070 } 071 072 protected void updateAvailablePlugin(PluginInformation other) { 073 if (other == null) return; 074 PluginInformation pi = getPluginInformation(other.name); 075 if (pi == null) { 076 availablePlugins.add(other); 077 return; 078 } 079 pi.updateFromPluginSite(other); 080 } 081 082 /** 083 * Updates the list of plugin information objects with new information from 084 * plugin update sites. 085 * 086 * @param fromPluginSite plugin information read from plugin update sites 087 */ 088 public void updateAvailablePlugins(Collection<PluginInformation> fromPluginSite) { 089 for (PluginInformation other: fromPluginSite) { 090 updateAvailablePlugin(other); 091 } 092 sort(); 093 filterDisplayedPlugins(filterExpression); 094 Set<String> activePlugins = new HashSet<String>(); 095 activePlugins.addAll(Main.pref.getCollection("plugins", activePlugins)); 096 for (PluginInformation pi: availablePlugins) { 097 if (selectedPluginsMap.get(pi) == null) { 098 if (activePlugins.contains(pi.name)) { 099 selectedPluginsMap.put(pi, true); 100 } 101 } 102 } 103 clearChanged(); 104 notifyObservers(); 105 } 106 107 /** 108 * Replies the list of selected plugin information objects 109 * 110 * @return the list of selected plugin information objects 111 */ 112 public List<PluginInformation> getSelectedPlugins() { 113 List<PluginInformation> ret = new LinkedList<PluginInformation>(); 114 for (PluginInformation pi: availablePlugins) { 115 if (selectedPluginsMap.get(pi) == null) { 116 continue; 117 } 118 if (selectedPluginsMap.get(pi)) { 119 ret.add(pi); 120 } 121 } 122 return ret; 123 } 124 125 /** 126 * Replies the list of selected plugin information objects 127 * 128 * @return the list of selected plugin information objects 129 */ 130 public Set<String> getSelectedPluginNames() { 131 Set<String> ret = new HashSet<String>(); 132 for (PluginInformation pi: getSelectedPlugins()) { 133 ret.add(pi.name); 134 } 135 return ret; 136 } 137 138 /** 139 * Sorts the list of available plugins 140 */ 141 protected void sort() { 142 Collections.sort( 143 availablePlugins, 144 new Comparator<PluginInformation>() { 145 public int compare(PluginInformation o1, PluginInformation o2) { 146 String n1 = o1.getName() == null ? "" : o1.getName().toLowerCase(); 147 String n2 = o2.getName() == null ? "" : o2.getName().toLowerCase(); 148 return n1.compareTo(n2); 149 } 150 } 151 ); 152 } 153 154 /** 155 * Replies the list of plugin informations to display 156 * 157 * @return the list of plugin informations to display 158 */ 159 public List<PluginInformation> getDisplayedPlugins() { 160 return displayedPlugins; 161 } 162 163 164 /** 165 * Replies the list of plugins waiting for update or download 166 * 167 * @return the list of plugins waiting for update or download 168 */ 169 public List<PluginInformation> getPluginsScheduledForUpdateOrDownload() { 170 List<PluginInformation> ret = new ArrayList<PluginInformation>(); 171 for (String plugin: pendingDownloads) { 172 PluginInformation pi = getPluginInformation(plugin); 173 if (pi == null) { 174 continue; 175 } 176 ret.add(pi); 177 } 178 return ret; 179 } 180 181 /** 182 * Sets whether the plugin is selected or not. 183 * 184 * @param name the name of the plugin 185 * @param selected true, if selected; false, otherwise 186 */ 187 public void setPluginSelected(String name, boolean selected) { 188 PluginInformation pi = getPluginInformation(name); 189 if (pi != null) { 190 selectedPluginsMap.put(pi,selected); 191 if (pi.isUpdateRequired()) { 192 pendingDownloads.add(pi.name); 193 } 194 } 195 if (!selected) { 196 pendingDownloads.remove(name); 197 } 198 } 199 200 /** 201 * Removes all the plugin in {@code plugins} from the list of plugins 202 * with a pending download 203 * 204 * @param plugins the list of plugins to clear for a pending download 205 */ 206 public void clearPendingPlugins(Collection<PluginInformation> plugins){ 207 if (plugins == null || plugins.isEmpty()) return; 208 for(PluginInformation pi: plugins) { 209 pendingDownloads.remove(pi.name); 210 } 211 } 212 213 /** 214 * Replies the plugin info with the name <code>name</code>. null, if no 215 * such plugin info exists. 216 * 217 * @param name the name. If null, replies null. 218 * @return the plugin info. 219 */ 220 public PluginInformation getPluginInformation(String name) { 221 for (PluginInformation pi: availablePlugins) { 222 if (pi.getName() != null && pi.getName().equals(name)) 223 return pi; 224 } 225 return null; 226 } 227 228 /** 229 * Initializes the model from preferences 230 */ 231 public void initFromPreferences() { 232 Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null); 233 if (enabledPlugins == null) { 234 this.selectedPluginsMap.clear(); 235 return; 236 } 237 for (String name: enabledPlugins) { 238 PluginInformation pi = getPluginInformation(name); 239 if (pi == null) { 240 continue; 241 } 242 setPluginSelected(name, true); 243 } 244 } 245 246 /** 247 * Replies true if the plugin with name <code>name</code> is currently 248 * selected in the plugin model 249 * 250 * @param name the plugin name 251 * @return true if the plugin is selected; false, otherwise 252 */ 253 public boolean isSelectedPlugin(String name) { 254 PluginInformation pi = getPluginInformation(name); 255 if (pi == null) return false; 256 if (selectedPluginsMap.get(pi) == null) return false; 257 return selectedPluginsMap.get(pi); 258 } 259 260 /** 261 * Replies the set of plugins which have been added by the user to 262 * the set of activated plugins. 263 * 264 * @return the set of newly deactivated plugins 265 */ 266 public List<PluginInformation> getNewlyActivatedPlugins() { 267 List<PluginInformation> ret = new LinkedList<PluginInformation>(); 268 for (Entry<PluginInformation, Boolean> entry: selectedPluginsMap.entrySet()) { 269 PluginInformation pi = entry.getKey(); 270 boolean selected = entry.getValue(); 271 if (selected && ! currentActivePlugins.contains(pi.name)) { 272 ret.add(pi); 273 } 274 } 275 return ret; 276 } 277 278 /** 279 * Replies the set of plugins which have been removed by the user from 280 * the set of activated plugins. 281 * 282 * @return the set of newly deactivated plugins 283 */ 284 public List<PluginInformation> getNewlyDeactivatedPlugins() { 285 List<PluginInformation> ret = new LinkedList<PluginInformation>(); 286 for (PluginInformation pi: availablePlugins) { 287 if (!currentActivePlugins.contains(pi.name)) { 288 continue; 289 } 290 if (selectedPluginsMap.get(pi) == null || ! selectedPluginsMap.get(pi)) { 291 ret.add(pi); 292 } 293 } 294 return ret; 295 } 296 297 /** 298 * Replies the set of all available plugins. 299 * 300 * @return the set of all available plugins 301 */ 302 public List<PluginInformation> getAvailablePlugins() { 303 return new LinkedList<PluginInformation>(availablePlugins); 304 } 305 306 /** 307 * Replies the set of plugin names which have been added by the user to 308 * the set of activated plugins. 309 * 310 * @return the set of newly activated plugin names 311 */ 312 public Set<String> getNewlyActivatedPluginNames() { 313 Set<String> ret = new HashSet<String>(); 314 List<PluginInformation> plugins = getNewlyActivatedPlugins(); 315 for (PluginInformation pi: plugins) { 316 ret.add(pi.name); 317 } 318 return ret; 319 } 320 321 /** 322 * Replies true if the set of active plugins has been changed by the user 323 * in this preference model. He has either added plugins or removed plugins 324 * being active before. 325 * 326 * @return true if the collection of active plugins has changed 327 */ 328 public boolean isActivePluginsChanged() { 329 Set<String> newActivePlugins = getSelectedPluginNames(); 330 return ! newActivePlugins.equals(currentActivePlugins); 331 } 332 333 /** 334 * Refreshes the local version field on the plugins in <code>plugins</code> with 335 * the version in the manifest of the downloaded "jar.new"-file for this plugin. 336 * 337 * @param plugins the collections of plugins to refresh 338 */ 339 public void refreshLocalPluginVersion(Collection<PluginInformation> plugins) { 340 if (plugins == null) return; 341 File pluginDir = Main.pref.getPluginsDirectory(); 342 for (PluginInformation pi : plugins) { 343 // Find the downloaded file. We have tried to install the downloaded plugins 344 // (PluginHandler.installDownloadedPlugins). This succeeds depending on the 345 // platform. 346 File downloadedPluginFile = new File(pluginDir, pi.name + ".jar.new"); 347 if (!(downloadedPluginFile.exists() && downloadedPluginFile.canRead())) { 348 downloadedPluginFile = new File(pluginDir, pi.name + ".jar"); 349 if (!(downloadedPluginFile.exists() && downloadedPluginFile.canRead())) { 350 continue; 351 } 352 } 353 try { 354 PluginInformation newinfo = new PluginInformation(downloadedPluginFile, pi.name); 355 PluginInformation oldinfo = getPluginInformation(pi.name); 356 if (oldinfo == null) { 357 // should not happen 358 continue; 359 } 360 oldinfo.localversion = newinfo.version; 361 } catch(PluginException e) { 362 e.printStackTrace(); 363 } 364 } 365 } 366 }