001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.actions;
003    
004    import java.io.File;
005    import java.util.ArrayList;
006    import java.util.Collections;
007    import java.util.Comparator;
008    import java.util.LinkedList;
009    import java.util.List;
010    
011    import javax.swing.JFileChooser;
012    import javax.swing.filechooser.FileFilter;
013    
014    import org.openstreetmap.josm.gui.MapView;
015    import org.openstreetmap.josm.io.AllFormatsImporter;
016    import org.openstreetmap.josm.io.FileExporter;
017    import org.openstreetmap.josm.io.FileImporter;
018    
019    /**
020     * A file filter that filters after the extension. Also includes a list of file
021     * filters used in JOSM.
022     * @since 32
023     */
024    public class ExtensionFileFilter extends FileFilter {
025    
026        /**
027         * List of supported formats for import.
028         * @since 4869
029         */
030        public static final ArrayList<FileImporter> importers;
031    
032        /**
033         * List of supported formats for export.
034         * @since 4869
035         */
036        public static final ArrayList<FileExporter> exporters;
037    
038        // add some file types only if the relevant classes are there;
039        // this gives us the option to painlessly drop them from the .jar
040        // and build JOSM versions without support for these formats
041    
042        static {
043    
044            importers = new ArrayList<FileImporter>();
045    
046            String[] importerNames = {
047                    "org.openstreetmap.josm.io.OsmImporter",
048                    "org.openstreetmap.josm.io.OsmGzipImporter",
049                    "org.openstreetmap.josm.io.OsmChangeImporter",
050                    "org.openstreetmap.josm.io.GpxImporter",
051                    "org.openstreetmap.josm.io.NMEAImporter",
052                    "org.openstreetmap.josm.io.OsmBzip2Importer",
053                    "org.openstreetmap.josm.io.JpgImporter",
054                    "org.openstreetmap.josm.io.WMSLayerImporter",
055                    "org.openstreetmap.josm.io.AllFormatsImporter"
056            };
057    
058            for (String classname : importerNames) {
059                try {
060                    FileImporter importer = (FileImporter) Class.forName(classname).newInstance();
061                    importers.add(importer);
062                    MapView.addLayerChangeListener(importer);
063                } catch (Throwable t) { }
064            }
065    
066            exporters = new ArrayList<FileExporter>();
067    
068            String[] exporterNames = {
069                    "org.openstreetmap.josm.io.GpxExporter",
070                    "org.openstreetmap.josm.io.OsmExporter",
071                    "org.openstreetmap.josm.io.OsmGzipExporter",
072                    "org.openstreetmap.josm.io.OsmBzip2Exporter",
073                    "org.openstreetmap.josm.io.GeoJSONExporter",
074                    "org.openstreetmap.josm.io.WMSLayerExporter"
075            };
076    
077            for (String classname : exporterNames) {
078                try {
079                    FileExporter exporter = (FileExporter)Class.forName(classname).newInstance();
080                    exporters.add(exporter);
081                    MapView.addLayerChangeListener(exporter);
082                } catch (Throwable t) { }
083            }
084        }
085    
086        private final String extensions;
087        private final String description;
088        private final String defaultExtension;
089    
090        static protected void sort(List<ExtensionFileFilter> filters) {
091            Collections.sort(
092                    filters,
093                    new Comparator<ExtensionFileFilter>() {
094                        private AllFormatsImporter all = new AllFormatsImporter();
095                        public int compare(ExtensionFileFilter o1, ExtensionFileFilter o2) {
096                            if (o1.getDescription().equals(all.filter.getDescription())) return 1;
097                            if (o2.getDescription().equals(all.filter.getDescription())) return -1;
098                            return o1.getDescription().compareTo(o2.getDescription());
099                        }
100                    }
101            );
102        }
103    
104        /**
105         * Updates the {@link AllFormatsImporter} that is contained in the importers list. If
106         * you do not use the importers variable directly, you don???t need to call this.
107         * <p>
108         * Updating the AllFormatsImporter is required when plugins add new importers that
109         * support new file extensions. The old AllFormatsImporter doesn???t include the new
110         * extensions and thus will not display these files.
111         * 
112         * @since 5131
113         */
114        public static void updateAllFormatsImporter() {
115            for(int i=0; i < importers.size(); i++) {
116                if(importers.get(i) instanceof AllFormatsImporter) {
117                    importers.set(i, new AllFormatsImporter());
118                }
119            }
120        }
121    
122        /**
123         * Replies an ordered list of {@link ExtensionFileFilter}s for importing.
124         * The list is ordered according to their description, an {@link AllFormatsImporter}
125         * is append at the end.
126         *
127         * @return an ordered list of {@link ExtensionFileFilter}s for importing.
128         * @since 2029
129         */
130        public static List<ExtensionFileFilter> getImportExtensionFileFilters() {
131            updateAllFormatsImporter();
132            LinkedList<ExtensionFileFilter> filters = new LinkedList<ExtensionFileFilter>();
133            for (FileImporter importer : importers) {
134                filters.add(importer.filter);
135            }
136            sort(filters);
137            return filters;
138        }
139    
140        /**
141         * Replies an ordered list of enabled {@link ExtensionFileFilter}s for exporting.
142         * The list is ordered according to their description, an {@link AllFormatsImporter}
143         * is append at the end.
144         *
145         * @return an ordered list of enabled {@link ExtensionFileFilter}s for exporting.
146         * @since 2029
147         */
148        public static List<ExtensionFileFilter> getExportExtensionFileFilters() {
149            LinkedList<ExtensionFileFilter> filters = new LinkedList<ExtensionFileFilter>();
150            for (FileExporter exporter : exporters) {
151                if (filters.contains(exporter.filter) || !exporter.isEnabled()) {
152                    continue;
153                }
154                filters.add(exporter.filter);
155            }
156            sort(filters);
157            return filters;
158        }
159    
160        /**
161         * Replies the default {@link ExtensionFileFilter} for a given extension
162         *
163         * @param extension the extension
164         * @return the default {@link ExtensionFileFilter} for a given extension
165         * @since 2029
166         */
167        public static ExtensionFileFilter getDefaultImportExtensionFileFilter(String extension) {
168            if (extension == null) return new AllFormatsImporter().filter;
169            for (FileImporter importer : importers) {
170                if (extension.equals(importer.filter.getDefaultExtension()))
171                    return importer.filter;
172            }
173            return new AllFormatsImporter().filter;
174        }
175    
176        /**
177         * Replies the default {@link ExtensionFileFilter} for a given extension
178         *
179         * @param extension the extension
180         * @return the default {@link ExtensionFileFilter} for a given extension
181         * @since 2029
182         */
183        public static ExtensionFileFilter getDefaultExportExtensionFileFilter(String extension) {
184            if (extension == null) return new AllFormatsImporter().filter;
185            for (FileExporter exporter : exporters) {
186                if (extension.equals(exporter.filter.getDefaultExtension()))
187                    return exporter.filter;
188            }
189            return new AllFormatsImporter().filter;
190        }
191    
192        /**
193         * Applies the choosable {@link FileFilter} to a {@link JFileChooser} before using the
194         * file chooser for selecting a file for reading.
195         *
196         * @param fileChooser the file chooser
197         * @param extension the default extension
198         * @param allTypes If true, all the files types known by JOSM will be proposed in the "file type" combobox. 
199         *                 If false, only the file filters that include {@code extension} will be proposed
200         * @since 5438
201         */
202        public static void applyChoosableImportFileFilters(JFileChooser fileChooser, String extension, boolean allTypes) {
203            for (ExtensionFileFilter filter: getImportExtensionFileFilters()) {
204                if (allTypes || filter.acceptName("file."+extension)) {
205                    fileChooser.addChoosableFileFilter(filter);
206                }
207            }
208            fileChooser.setFileFilter(getDefaultImportExtensionFileFilter(extension));
209        }
210    
211        /**
212         * Applies the choosable {@link FileFilter} to a {@link JFileChooser} before using the
213         * file chooser for selecting a file for writing.
214         *
215         * @param fileChooser the file chooser
216         * @param extension the default extension
217         * @param allTypes If true, all the files types known by JOSM will be proposed in the "file type" combobox. 
218         *                 If false, only the file filters that include {@code extension} will be proposed
219         * @since 5438
220         */
221        public static void applyChoosableExportFileFilters(JFileChooser fileChooser, String extension, boolean allTypes) {
222            for (ExtensionFileFilter filter: getExportExtensionFileFilters()) {
223                if (allTypes || filter.acceptName("file."+extension)) {
224                    fileChooser.addChoosableFileFilter(filter);
225                }
226            }
227            fileChooser.setFileFilter(getDefaultExportExtensionFileFilter(extension));
228        }
229    
230        /**
231         * Construct an extension file filter by giving the extension to check after.
232         * @param extension The comma-separated list of file extensions
233         * @param defaultExtension The default extension
234         * @param description A short textual description of the file type
235         * @since 1169
236         */
237        public ExtensionFileFilter(String extension, String defaultExtension, String description) {
238            this.extensions = extension;
239            this.defaultExtension = defaultExtension;
240            this.description = description;
241        }
242    
243        /**
244         * Returns true if this file filter accepts the given filename.
245         * @param filename The filename to check after
246         * @return true if this file filter accepts the given filename (i.e if this filename ends with one of the extensions)
247         * @since 1169
248         */
249        public boolean acceptName(String filename) {
250            String name = filename.toLowerCase();
251            for (String ext : extensions.split(","))
252                if (name.endsWith("."+ext))
253                    return true;
254            return false;
255        }
256    
257        @Override
258        public boolean accept(File pathname) {
259            if (pathname.isDirectory())
260                return true;
261            return acceptName(pathname.getName());
262        }
263    
264        @Override
265        public String getDescription() {
266            return description;
267        }
268    
269        /**
270         * Replies the comma-separated list of file extensions of this file filter. 
271         * @return the comma-separated list of file extensions of this file filter, as a String
272         * @since 5131
273         */
274        public String getExtensions() {
275            return extensions;
276        }
277    
278        /**
279         * Replies the default file extension of this file filter.
280         * @return the default file extension of this file filter
281         * @since 2029
282         */
283        public String getDefaultExtension() {
284            return defaultExtension;
285        }
286    
287        @Override
288        public int hashCode() {
289            final int prime = 31;
290            int result = 1;
291            result = prime * result + ((defaultExtension == null) ? 0 : defaultExtension.hashCode());
292            result = prime * result + ((description == null) ? 0 : description.hashCode());
293            result = prime * result + ((extensions == null) ? 0 : extensions.hashCode());
294            return result;
295        }
296    
297        @Override
298        public boolean equals(Object obj) {
299            if (this == obj)
300                return true;
301            if (obj == null)
302                return false;
303            if (getClass() != obj.getClass())
304                return false;
305            ExtensionFileFilter other = (ExtensionFileFilter) obj;
306            if (defaultExtension == null) {
307                if (other.defaultExtension != null)
308                    return false;
309            } else if (!defaultExtension.equals(other.defaultExtension))
310                return false;
311            if (description == null) {
312                if (other.description != null)
313                    return false;
314            } else if (!description.equals(other.description))
315                return false;
316            if (extensions == null) {
317                if (other.extensions != null)
318                    return false;
319            } else if (!extensions.equals(other.extensions))
320                return false;
321            return true;
322        }
323    }