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