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.gui.util.ChangeNotifier; 018import org.openstreetmap.josm.plugins.PluginException; 019import org.openstreetmap.josm.plugins.PluginHandler; 020import org.openstreetmap.josm.plugins.PluginInformation; 021import org.openstreetmap.josm.spi.preferences.Config; 022import org.openstreetmap.josm.tools.Logging; 023 024/** 025 * The plugin model behind a {@code PluginListPanel}. 026 */ 027public class PluginPreferencesModel extends ChangeNotifier { 028 // remember the initial list of active plugins 029 private final Set<String> currentActivePlugins; 030 private final List<PluginInformation> availablePlugins = new ArrayList<>(); 031 private String filterExpression; 032 private final List<PluginInformation> displayedPlugins = new ArrayList<>(); 033 private final Map<PluginInformation, Boolean> selectedPluginsMap = new HashMap<>(); 034 // plugins that still require an update/download 035 private final Set<String> pendingDownloads = new HashSet<>(); 036 037 /** 038 * Constructs a new {@code PluginPreferencesModel}. 039 */ 040 public PluginPreferencesModel() { 041 currentActivePlugins = new HashSet<>(); 042 currentActivePlugins.addAll(Config.getPref().getList("plugins")); 043 } 044 045 /** 046 * Filters the list of displayed plugins. 047 * @param filter The filter used against plugin name, description or version 048 */ 049 public void filterDisplayedPlugins(String filter) { 050 if (filter == null) { 051 displayedPlugins.clear(); 052 displayedPlugins.addAll(availablePlugins); 053 this.filterExpression = null; 054 return; 055 } 056 displayedPlugins.clear(); 057 for (PluginInformation pi: availablePlugins) { 058 if (pi.matches(filter)) { 059 displayedPlugins.add(pi); 060 } 061 } 062 filterExpression = filter; 063 fireStateChanged(); 064 } 065 066 /** 067 * Sets the list of available plugins. 068 * @param available The available plugins 069 */ 070 public void setAvailablePlugins(Collection<PluginInformation> available) { 071 availablePlugins.clear(); 072 if (available != null) { 073 availablePlugins.addAll(available); 074 } 075 availablePluginsModified(); 076 } 077 078 protected final void availablePluginsModified() { 079 sort(); 080 filterDisplayedPlugins(filterExpression); 081 Set<String> activePlugins = new HashSet<>(); 082 activePlugins.addAll(Config.getPref().getList("plugins")); 083 for (PluginInformation pi: availablePlugins) { 084 if (selectedPluginsMap.get(pi) == null && activePlugins.contains(pi.name)) { 085 selectedPluginsMap.put(pi, Boolean.TRUE); 086 } 087 } 088 fireStateChanged(); 089 } 090 091 protected void updateAvailablePlugin(PluginInformation other) { 092 if (other != null) { 093 PluginInformation pi = getPluginInformation(other.name); 094 if (pi == null) { 095 availablePlugins.add(other); 096 return; 097 } 098 pi.updateFromPluginSite(other); 099 } 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 availablePlugins.sort(Comparator.comparing( 151 o -> o.getName() == null ? "" : o.getName().toLowerCase(Locale.ENGLISH))); 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 * Replies the set of plugins waiting for update or download. 165 * 166 * @return the set of plugins waiting for update or download 167 */ 168 public Set<PluginInformation> getPluginsScheduledForUpdateOrDownload() { 169 Set<PluginInformation> ret = new HashSet<>(); 170 for (String plugin: pendingDownloads) { 171 PluginInformation pi = getPluginInformation(plugin); 172 if (pi == null) { 173 continue; 174 } 175 ret.add(pi); 176 } 177 return ret; 178 } 179 180 /** 181 * Sets whether the plugin is selected or not. 182 * 183 * @param name the name of the plugin 184 * @param selected true, if selected; false, otherwise 185 */ 186 public void setPluginSelected(String name, boolean selected) { 187 PluginInformation pi = getPluginInformation(name); 188 if (pi != null) { 189 selectedPluginsMap.put(pi, selected); 190 if (pi.isUpdateRequired()) { 191 pendingDownloads.add(pi.name); 192 } 193 } 194 if (!selected) { 195 pendingDownloads.remove(name); 196 } 197 } 198 199 /** 200 * Removes all the plugin in {@code plugins} from the list of plugins 201 * with a pending download 202 * 203 * @param plugins the list of plugins to clear for a pending download 204 */ 205 public void clearPendingPlugins(Collection<PluginInformation> plugins) { 206 if (plugins != null) { 207 for (PluginInformation pi: plugins) { 208 pendingDownloads.remove(pi.name); 209 } 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 = Config.getPref().getList("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 || selectedPluginsMap.get(pi) == null) 256 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 activated plugins 265 */ 266 public List<PluginInformation> getNewlyActivatedPlugins() { 267 List<PluginInformation> ret = new LinkedList<>(); 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 deactivated plugins. 281 * 282 * @return the set of newly deactivated plugins 283 */ 284 public List<PluginInformation> getNewlyDeactivatedPlugins() { 285 List<PluginInformation> ret = new LinkedList<>(); 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<>(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<>(); 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) { 341 for (PluginInformation pi : plugins) { 342 File downloadedPluginFile = PluginHandler.findUpdatedJar(pi.name); 343 if (downloadedPluginFile == null) { 344 continue; 345 } 346 try { 347 PluginInformation newinfo = new PluginInformation(downloadedPluginFile, pi.name); 348 PluginInformation oldinfo = getPluginInformation(pi.name); 349 if (oldinfo != null) { 350 oldinfo.updateLocalInfo(newinfo); 351 } 352 } catch (PluginException e) { 353 Logging.error(e); 354 } 355 } 356 } 357 } 358}