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