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