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