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 }