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