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    }