Index: trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java	(revision 12671)
@@ -17,15 +17,15 @@
 
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.io.importexport.AllFormatsImporter;
+import org.openstreetmap.josm.gui.io.importexport.FileExporter;
+import org.openstreetmap.josm.gui.io.importexport.FileImporter;
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
+import org.openstreetmap.josm.gui.io.importexport.JpgImporter;
+import org.openstreetmap.josm.gui.io.importexport.NMEAImporter;
+import org.openstreetmap.josm.gui.io.importexport.NoteImporter;
+import org.openstreetmap.josm.gui.io.importexport.OsmChangeImporter;
+import org.openstreetmap.josm.gui.io.importexport.OsmImporter;
+import org.openstreetmap.josm.gui.io.importexport.WMSLayerImporter;
 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
-import org.openstreetmap.josm.io.AllFormatsImporter;
-import org.openstreetmap.josm.io.FileExporter;
-import org.openstreetmap.josm.io.FileImporter;
-import org.openstreetmap.josm.io.GpxImporter;
-import org.openstreetmap.josm.io.JpgImporter;
-import org.openstreetmap.josm.io.NMEAImporter;
-import org.openstreetmap.josm.io.NoteImporter;
-import org.openstreetmap.josm.io.OsmChangeImporter;
-import org.openstreetmap.josm.io.OsmImporter;
-import org.openstreetmap.josm.io.WMSLayerImporter;
 import org.openstreetmap.josm.io.session.SessionImporter;
 import org.openstreetmap.josm.tools.Logging;
@@ -99,12 +99,12 @@
 
         final List<Class<? extends FileExporter>> exporterClasses = Arrays.asList(
-                org.openstreetmap.josm.io.GpxExporter.class,
-                org.openstreetmap.josm.io.OsmExporter.class,
-                org.openstreetmap.josm.io.OsmGzipExporter.class,
-                org.openstreetmap.josm.io.OsmBzip2Exporter.class,
-                org.openstreetmap.josm.io.GeoJSONExporter.class,
-                org.openstreetmap.josm.io.WMSLayerExporter.class,
-                org.openstreetmap.josm.io.NoteExporter.class,
-                org.openstreetmap.josm.io.ValidatorErrorExporter.class
+                org.openstreetmap.josm.gui.io.importexport.GpxExporter.class,
+                org.openstreetmap.josm.gui.io.importexport.OsmExporter.class,
+                org.openstreetmap.josm.gui.io.importexport.OsmGzipExporter.class,
+                org.openstreetmap.josm.gui.io.importexport.OsmBzip2Exporter.class,
+                org.openstreetmap.josm.gui.io.importexport.GeoJSONExporter.class,
+                org.openstreetmap.josm.gui.io.importexport.WMSLayerExporter.class,
+                org.openstreetmap.josm.gui.io.importexport.NoteExporter.class,
+                org.openstreetmap.josm.gui.io.importexport.ValidatorErrorExporter.class
         );
 
Index: trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 12671)
@@ -15,9 +15,9 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.io.importexport.FileExporter;
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.io.FileExporter;
-import org.openstreetmap.josm.io.GpxImporter;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Logging;
Index: trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java	(revision 12671)
@@ -36,7 +36,7 @@
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.io.importexport.AllFormatsImporter;
+import org.openstreetmap.josm.gui.io.importexport.FileImporter;
 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
-import org.openstreetmap.josm.io.AllFormatsImporter;
-import org.openstreetmap.josm.io.FileImporter;
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.Logging;
Index: trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 12671)
@@ -17,8 +17,8 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.io.importexport.FileExporter;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
-import org.openstreetmap.josm.io.FileExporter;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Shortcut;
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java	(revision 12671)
@@ -21,4 +21,6 @@
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter.GpxImporterData;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
@@ -28,6 +30,4 @@
 import org.openstreetmap.josm.gui.progress.ProgressTaskIds;
 import org.openstreetmap.josm.io.BoundingBoxDownloader;
-import org.openstreetmap.josm.io.GpxImporter;
-import org.openstreetmap.josm.io.GpxImporter.GpxImporterData;
 import org.openstreetmap.josm.io.OsmServerLocationReader;
 import org.openstreetmap.josm.io.OsmServerReader;
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/AllFormatsImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/AllFormatsImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/AllFormatsImporter.java	(revision 12671)
@@ -0,0 +1,45 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.util.Iterator;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+
+/**
+ * Dummy importer that adds the "All Formats"-Filter when opening files
+ */
+public class AllFormatsImporter extends FileImporter {
+    /**
+     * Constructs a new {@code AllFormatsImporter}.
+     */
+    public AllFormatsImporter() {
+        super(new ExtensionFileFilter(getAllExtensions(), "", tr("All Formats")
+                + " (*.gpx *.osm *.nmea *.jpg ...)"));
+    }
+
+    @Override
+    public boolean acceptFile(File pathname) {
+        return false;
+    }
+
+    /**
+     * Builds list of all supported extensions by the registered FileImporters.
+     * @return String comma separated list of supported file extensions
+     */
+    private static String getAllExtensions() {
+        Iterator<FileImporter> imp = ExtensionFileFilter.getImporters().iterator();
+        StringBuilder ext = new StringBuilder();
+        while (imp.hasNext()) {
+            FileImporter fi = imp.next();
+            if (fi instanceof AllFormatsImporter) {
+                continue;
+            }
+            ext.append(fi.filter.getExtensions()).append(',');
+        }
+        // remove last comma
+        return ext.substring(0, ext.length()-1);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/FileExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/FileExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/FileExporter.java	(revision 12671)
@@ -0,0 +1,96 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
+import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
+
+/**
+ * Abstract base class for file exporters - IO classes that save layers to a file.
+ */
+public abstract class FileExporter implements ActiveLayerChangeListener {
+
+    public final ExtensionFileFilter filter;
+
+    private boolean enabled;
+    private boolean canceled;
+
+    /**
+     * Constructs a new {@code FileExporter}.
+     * @param filter The extension file filter
+     */
+    public FileExporter(ExtensionFileFilter filter) {
+        this.filter = filter;
+        this.enabled = true;
+    }
+
+    /**
+     * Check if this exporter can export a certain layer to a certain file.
+     *
+     * Most exporters support just a single layer type.
+     * @param pathname the target file name (check file extension using the {@link #filter}
+     * @param layer the layer requested for export
+     * @return true, if the exporter can handle the layer and filename is okay
+     */
+    public boolean acceptFile(File pathname, Layer layer) {
+        return filter.acceptName(pathname.getName());
+    }
+
+    /**
+     * Execute the data export. (To be overridden by subclasses.)
+     *
+     * @param file target file
+     * @param layer the layer to export
+     * @throws IOException in case of an IO error
+     */
+    public void exportData(File file, Layer layer) throws IOException {
+        throw new IOException(tr("Could not export ''{0}''.", file.getName()));
+    }
+
+    /**
+     * Returns the enabled state of this {@code FileExporter}. When enabled, it is listed and usable in "File-&gt;Save" dialogs.
+     * @return true if this {@code FileExporter} is enabled
+     * @since 5459
+     */
+    public final boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * Sets the enabled state of the {@code FileExporter}. When enabled, it is listed and usable in "File-&gt;Save" dialogs.
+     * @param enabled true to enable this {@code FileExporter}, false to disable it
+     * @since 5459
+     */
+    public final void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    @Override
+    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
+        // To be overriden by subclasses if their enabled state depends of the active layer nature
+    }
+
+    /**
+     * Determines if this exporter has been canceled during export.
+     * @return true if this {@code FileExporter} has been canceled
+     * @since 6815
+     */
+    public final boolean isCanceled() {
+        return canceled;
+    }
+
+    /**
+     * Marks this exporter as canceled.
+     * @param canceled true to mark this exporter as canceled, {@code false} otherwise
+     * @since 6815
+     */
+    public final void setCanceled(boolean canceled) {
+        this.canceled = canceled;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/FileImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/FileImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/FileImporter.java	(revision 12671)
@@ -0,0 +1,189 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.Notification;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.io.ImportCancelException;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
+
+/**
+ * Abstract file importer.
+ * @since 1637
+ * @since 10386 (signature)
+ */
+public abstract class FileImporter implements Comparable<FileImporter> {
+
+    /**
+     * The extension file filter used to accept files.
+     */
+    public final ExtensionFileFilter filter;
+
+    private boolean enabled;
+
+    /**
+     * Constructs a new {@code FileImporter} with the given extension file filter.
+     * @param filter The extension file filter
+     */
+    public FileImporter(ExtensionFileFilter filter) {
+        this.filter = filter;
+        this.enabled = true;
+    }
+
+    /**
+     * Determines if this file importer accepts the given file.
+     * @param pathname The file to test
+     * @return {@code true} if this file importer accepts the given file, {@code false} otherwise
+     */
+    public boolean acceptFile(File pathname) {
+        return filter.acceptName(pathname.getName());
+    }
+
+    /**
+     * A batch importer is a file importer that prefers to read multiple files at the same time.
+     * @return {@code true} if this importer is a batch importer
+     */
+    public boolean isBatchImporter() {
+        return false;
+    }
+
+    /**
+     * Needs to be implemented if isBatchImporter() returns false.
+     * @param file file to import
+     * @param progressMonitor progress monitor
+     * @throws IOException if any I/O error occurs
+     * @throws IllegalDataException if invalid data is read
+     */
+    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
+        throw new IOException(tr("Could not import ''{0}''.", file.getName()));
+    }
+
+    /**
+     * Needs to be implemented if isBatchImporter() returns true.
+     * @param files files to import
+     * @param progressMonitor progress monitor
+     * @throws IOException if any I/O error occurs
+     * @throws IllegalDataException if invalid data is read
+     */
+    public void importData(List<File> files, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
+        throw new IOException(tr("Could not import files."));
+    }
+
+    /**
+     * Wrapper to {@link #importData(File, ProgressMonitor)} to give meaningful output if things go wrong.
+     * @param f data file to import
+     * @param progressMonitor progress monitor
+     * @return true if data import was successful
+     */
+    public boolean importDataHandleExceptions(File f, ProgressMonitor progressMonitor) {
+        try {
+            Logging.info("Open file: " + f.getAbsolutePath() + " (" + f.length() + " bytes)");
+            importData(f, progressMonitor);
+            return true;
+        } catch (IllegalDataException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof ImportCancelException) {
+                displayCancel(cause);
+            } else {
+                displayError(f, e);
+            }
+            return false;
+        } catch (IOException e) {
+            displayError(f, e);
+            return false;
+        } catch (RuntimeException | LinkageError e) { // NOPMD
+            BugReportExceptionHandler.handleException(e);
+            return false;
+        }
+    }
+
+    private static void displayError(File f, Exception e) {
+        Logging.error(e);
+        HelpAwareOptionPane.showMessageDialogInEDT(
+                Main.parent,
+                tr("<html>Could not read file ''{0}''.<br>Error is:<br>{1}</html>",
+                        f.getName(), Utils.escapeReservedCharactersHTML(e.getMessage())),
+                tr("Error"),
+                JOptionPane.ERROR_MESSAGE, null
+        );
+    }
+
+    private static void displayCancel(final Throwable t) {
+        GuiHelper.runInEDTAndWait(() -> {
+            Notification note = new Notification(t.getMessage());
+            note.setIcon(JOptionPane.INFORMATION_MESSAGE);
+            note.setDuration(Notification.TIME_SHORT);
+            note.show();
+        });
+    }
+
+    /**
+     * Wrapper to {@link #importData(List, ProgressMonitor)} to give meaningful output if things go wrong.
+     * @param files data files to import
+     * @param progressMonitor progress monitor
+     * @return true if data import was successful
+     */
+    public boolean importDataHandleExceptions(List<File> files, ProgressMonitor progressMonitor) {
+        try {
+            Logging.info("Open "+files.size()+" files");
+            importData(files, progressMonitor);
+            return true;
+        } catch (IOException | IllegalDataException e) {
+            Logging.error(e);
+            HelpAwareOptionPane.showMessageDialogInEDT(
+                    Main.parent,
+                    tr("<html>Could not read files.<br>Error is:<br>{0}</html>", Utils.escapeReservedCharactersHTML(e.getMessage())),
+                    tr("Error"),
+                    JOptionPane.ERROR_MESSAGE, null
+            );
+            return false;
+        }
+    }
+
+    /**
+     * If multiple files (with multiple file formats) are selected,
+     * they are opened in the order of their priorities.
+     * Highest priority comes first.
+     * @return priority
+     */
+    public double getPriority() {
+        return 0;
+    }
+
+    @Override
+    public int compareTo(FileImporter other) {
+        return Double.compare(this.getPriority(), other.getPriority());
+    }
+
+    /**
+     * Returns the enabled state of this {@code FileImporter}. When enabled, it is listed and usable in "File-&gt;Open" dialog.
+     * @return true if this {@code FileImporter} is enabled
+     * @since 5459
+     */
+    public final boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * Sets the enabled state of the {@code FileImporter}. When enabled, it is listed and usable in "File-&gt;Open" dialog.
+     * @param enabled true to enable this {@code FileImporter}, false to disable it
+     * @since 5459
+     */
+    public final void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/GeoJSONExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/GeoJSONExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/GeoJSONExporter.java	(revision 12671)
@@ -0,0 +1,44 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.io.GeoJSONWriter;
+
+/**
+ * Exporter to write map data to a GeoJSON file.
+ * @since 4886
+ */
+public class GeoJSONExporter extends FileExporter {
+
+    /** File extension filter for .geojson files */
+    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
+            "geojson,json", "geojson", tr("GeoJSON Files") + " (*.geojson *.json)");
+
+    /**
+     * Constructs a new {@code GeoJSONExporter} with WGS84 projection.
+     */
+    public GeoJSONExporter() {
+        super(FILE_FILTER);
+    }
+
+    @Override
+    public void exportData(File file, Layer layer) throws IOException {
+        if (layer instanceof OsmDataLayer) {
+            try (Writer out = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
+                out.write(new GeoJSONWriter((OsmDataLayer) layer).write());
+            }
+        } else {
+            throw new IllegalArgumentException(tr("Layer ''{0}'' not supported", layer.getClass().toString()));
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java	(revision 12671)
@@ -0,0 +1,333 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.time.Year;
+import java.util.Optional;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.gpx.GpxConstants;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.widgets.JosmTextArea;
+import org.openstreetmap.josm.gui.widgets.JosmTextField;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.GpxWriter;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Exports data to a .gpx file. Data may be native GPX or OSM data which will be converted.
+ * @since 1949
+ */
+public class GpxExporter extends FileExporter implements GpxConstants {
+
+    private static final String GPL_WARNING = "<html><font color='red' size='-2'>"
+        + tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.") + "</html>";
+
+    private static final String[] LICENSES = {
+            "Creative Commons By-SA",
+            "Open Database License (ODbL)",
+            "public domain",
+            "GNU Lesser Public License (LGPL)",
+            "BSD License (MIT/X11)"};
+
+    private static final String[] URLS = {
+            "https://creativecommons.org/licenses/by-sa/3.0",
+            "http://opendatacommons.org/licenses/odbl/1.0",
+            "public domain",
+            "https://www.gnu.org/copyleft/lesser.html",
+            "http://www.opensource.org/licenses/bsd-license.php"};
+
+    /**
+     * Constructs a new {@code GpxExporter}.
+     */
+    public GpxExporter() {
+        super(GpxImporter.getFileFilter());
+    }
+
+    @Override
+    public boolean acceptFile(File pathname, Layer layer) {
+        if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer))
+            return false;
+        return super.acceptFile(pathname, layer);
+    }
+
+    @Override
+    public void exportData(File file, Layer layer) throws IOException {
+        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
+        if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer))
+            throw new IllegalArgumentException(MessageFormat.format("Expected instance of OsmDataLayer or GpxLayer. Got ''{0}''.", layer
+                    .getClass().getName()));
+        CheckParameterUtil.ensureParameterNotNull(file, "file");
+
+        String fn = file.getPath();
+        if (fn.indexOf('.') == -1) {
+            fn += ".gpx";
+            file = new File(fn);
+        }
+
+        // open the dialog asking for options
+        JPanel p = new JPanel(new GridBagLayout());
+
+        GpxData gpxData;
+        // At this moment, we only need to know the attributes of the GpxData,
+        // conversion of OsmDataLayer (if needed) will be done after the dialog is closed.
+        if (layer instanceof GpxLayer) {
+            gpxData = ((GpxLayer) layer).data;
+        } else {
+            gpxData = new GpxData();
+        }
+
+        p.add(new JLabel(tr("GPS track description")), GBC.eol());
+        JosmTextArea desc = new JosmTextArea(3, 40);
+        desc.setWrapStyleWord(true);
+        desc.setLineWrap(true);
+        desc.setText(gpxData.getString(META_DESC));
+        p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH));
+
+        JCheckBox author = new JCheckBox(tr("Add author information"), Main.pref.getBoolean("lastAddAuthor", true));
+        p.add(author, GBC.eol());
+
+        JLabel nameLabel = new JLabel(tr("Real name"));
+        p.add(nameLabel, GBC.std().insets(10, 0, 5, 0));
+        JosmTextField authorName = new JosmTextField();
+        p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL));
+        nameLabel.setLabelFor(authorName);
+
+        JLabel emailLabel = new JLabel(tr("E-Mail"));
+        p.add(emailLabel, GBC.std().insets(10, 0, 5, 0));
+        JosmTextField email = new JosmTextField();
+        p.add(email, GBC.eol().fill(GBC.HORIZONTAL));
+        emailLabel.setLabelFor(email);
+
+        JLabel copyrightLabel = new JLabel(tr("Copyright (URL)"));
+        p.add(copyrightLabel, GBC.std().insets(10, 0, 5, 0));
+        JosmTextField copyright = new JosmTextField();
+        p.add(copyright, GBC.std().fill(GBC.HORIZONTAL));
+        copyrightLabel.setLabelFor(copyright);
+
+        JButton predefined = new JButton(tr("Predefined"));
+        p.add(predefined, GBC.eol().insets(5, 0, 0, 0));
+
+        JLabel copyrightYearLabel = new JLabel(tr("Copyright year"));
+        p.add(copyrightYearLabel, GBC.std().insets(10, 0, 5, 5));
+        JosmTextField copyrightYear = new JosmTextField("");
+        p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL));
+        copyrightYearLabel.setLabelFor(copyrightYear);
+
+        JLabel warning = new JLabel("<html><font size='-2'>&nbsp;</html");
+        p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15, 0, 0, 0));
+        addDependencies(gpxData, author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel,
+                copyrightLabel, copyrightYearLabel, warning);
+
+        p.add(new JLabel(tr("Keywords")), GBC.eol());
+        JosmTextField keywords = new JosmTextField();
+        keywords.setText(gpxData.getString(META_KEYWORDS));
+        p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL));
+
+        ExtendedDialog ed = new ExtendedDialog(Main.parent,
+                tr("Export options"),
+                tr("Export and Save"), tr("Cancel"))
+            .setButtonIcons("exportgpx", "cancel")
+            .setContent(p);
+
+        if (ed.showDialog().getValue() != 1) {
+            setCanceled(true);
+            return;
+        }
+        setCanceled(false);
+
+        Main.pref.put("lastAddAuthor", author.isSelected());
+        if (!authorName.getText().isEmpty()) {
+            Main.pref.put("lastAuthorName", authorName.getText());
+        }
+        if (!copyright.getText().isEmpty()) {
+            Main.pref.put("lastCopyright", copyright.getText());
+        }
+
+        if (layer instanceof OsmDataLayer) {
+            gpxData = ((OsmDataLayer) layer).toGpxData();
+        } else if (layer instanceof GpxLayer) {
+            gpxData = ((GpxLayer) layer).data;
+        } else {
+            gpxData = OsmDataLayer.toGpxData(MainApplication.getLayerManager().getEditDataSet(), file);
+        }
+
+        // add author and copyright details to the gpx data
+        if (author.isSelected()) {
+            if (!authorName.getText().isEmpty()) {
+                gpxData.put(META_AUTHOR_NAME, authorName.getText());
+                gpxData.put(META_COPYRIGHT_AUTHOR, authorName.getText());
+            }
+            if (!email.getText().isEmpty()) {
+                gpxData.put(META_AUTHOR_EMAIL, email.getText());
+            }
+            if (!copyright.getText().isEmpty()) {
+                gpxData.put(META_COPYRIGHT_LICENSE, copyright.getText());
+            }
+            if (!copyrightYear.getText().isEmpty()) {
+                gpxData.put(META_COPYRIGHT_YEAR, copyrightYear.getText());
+            }
+        }
+
+        // add the description to the gpx data
+        if (!desc.getText().isEmpty()) {
+            gpxData.put(META_DESC, desc.getText());
+        }
+
+        // add keywords to the gpx data
+        if (!keywords.getText().isEmpty()) {
+            gpxData.put(META_KEYWORDS, keywords.getText());
+        }
+
+        try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) {
+            new GpxWriter(fo).write(gpxData);
+            fo.flush();
+        } catch (IOException ex) {
+            Logging.error(ex);
+            JOptionPane.showMessageDialog(Main.parent, tr("Error while exporting {0}:\n{1}", fn, ex.getMessage()),
+                    tr("Error"), JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+    private static void enableCopyright(final GpxData data, final JosmTextField copyright, final JButton predefined,
+            final JosmTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel,
+            final JLabel warning, boolean enable) {
+        copyright.setEnabled(enable);
+        predefined.setEnabled(enable);
+        copyrightYear.setEnabled(enable);
+        copyrightLabel.setEnabled(enable);
+        copyrightYearLabel.setEnabled(enable);
+        warning.setText(enable ? GPL_WARNING : "<html><font size='-2'>&nbsp;</html");
+
+        if (enable) {
+            if (copyrightYear.getText().isEmpty()) {
+                copyrightYear.setText(Optional.ofNullable(data.getString(META_COPYRIGHT_YEAR)).orElseGet(
+                        () -> Year.now().toString()));
+            }
+            if (copyright.getText().isEmpty()) {
+                copyright.setText(Optional.ofNullable(data.getString(META_COPYRIGHT_LICENSE)).orElseGet(
+                        () -> Main.pref.get("lastCopyright", "https://creativecommons.org/licenses/by-sa/2.5")));
+                copyright.setCaretPosition(0);
+            }
+        } else {
+            copyrightYear.setText("");
+            copyright.setText("");
+        }
+    }
+
+    // CHECKSTYLE.OFF: ParameterNumber
+
+    /**
+     * Add all those listeners to handle the enable state of the fields.
+     * @param data GPX data
+     * @param author Author checkbox
+     * @param authorName Author name textfield
+     * @param email E-mail textfield
+     * @param copyright Copyright textfield
+     * @param predefined Predefined button
+     * @param copyrightYear Copyright year textfield
+     * @param nameLabel Name label
+     * @param emailLabel E-mail label
+     * @param copyrightLabel Copyright label
+     * @param copyrightYearLabel Copyright year label
+     * @param warning Warning label
+     */
+    private static void addDependencies(
+            final GpxData data,
+            final JCheckBox author,
+            final JosmTextField authorName,
+            final JosmTextField email,
+            final JosmTextField copyright,
+            final JButton predefined,
+            final JosmTextField copyrightYear,
+            final JLabel nameLabel,
+            final JLabel emailLabel,
+            final JLabel copyrightLabel,
+            final JLabel copyrightYearLabel,
+            final JLabel warning) {
+
+        // CHECKSTYLE.ON: ParameterNumber
+        ActionListener authorActionListener = e -> {
+            boolean b = author.isSelected();
+            authorName.setEnabled(b);
+            email.setEnabled(b);
+            nameLabel.setEnabled(b);
+            emailLabel.setEnabled(b);
+            if (b) {
+                authorName.setText(Optional.ofNullable(data.getString(META_AUTHOR_NAME)).orElseGet(() -> Main.pref.get("lastAuthorName")));
+                email.setText(Optional.ofNullable(data.getString(META_AUTHOR_EMAIL)).orElseGet(() -> Main.pref.get("lastAuthorEmail")));
+            } else {
+                authorName.setText("");
+                email.setText("");
+            }
+            boolean isAuthorSet = !authorName.getText().isEmpty();
+            GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning,
+                    b && isAuthorSet);
+        };
+        author.addActionListener(authorActionListener);
+
+        KeyAdapter authorNameListener = new KeyAdapter() {
+            @Override public void keyReleased(KeyEvent e) {
+                boolean b = !authorName.getText().isEmpty() && author.isSelected();
+                GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b);
+            }
+        };
+        authorName.addKeyListener(authorNameListener);
+
+        predefined.addActionListener(e -> {
+            JList<String> l = new JList<>(LICENSES);
+            l.setVisibleRowCount(LICENSES.length);
+            l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+            int answer = JOptionPane.showConfirmDialog(
+                    Main.parent,
+                    new JScrollPane(l),
+                    tr("Choose a predefined license"),
+                    JOptionPane.OK_CANCEL_OPTION,
+                    JOptionPane.QUESTION_MESSAGE
+            );
+            if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1)
+                return;
+            StringBuilder license = new StringBuilder();
+            for (int i : l.getSelectedIndices()) {
+                if (i == 2) {
+                    license = new StringBuilder("public domain");
+                    break;
+                }
+                if (license.length() > 0) {
+                    license.append(", ");
+                }
+                license.append(URLS[i]);
+            }
+            copyright.setText(license.toString());
+            copyright.setCaretPosition(0);
+        });
+
+        authorActionListener.actionPerformed(null);
+        authorNameListener.keyReleased(null);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java	(revision 12671)
@@ -0,0 +1,194 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.GpxReader;
+import org.openstreetmap.josm.tools.Logging;
+import org.xml.sax.SAXException;
+
+/**
+ * File importer allowing to import GPX files (*.gpx/gpx.gz files).
+ *
+ */
+public class GpxImporter extends FileImporter {
+
+    /**
+     * Utility class containing imported GPX and marker layers, and a task to run after they are added to MapView.
+     */
+    public static class GpxImporterData {
+        /**
+         * The imported GPX layer. May be null if no GPX data.
+         */
+        private final GpxLayer gpxLayer;
+        /**
+         * The imported marker layer. May be null if no marker.
+         */
+        private final MarkerLayer markerLayer;
+        /**
+         * The task to run after GPX and/or marker layer has been added to MapView.
+         */
+        private final Runnable postLayerTask;
+
+        /**
+         * Constructs a new {@code GpxImporterData}.
+         * @param gpxLayer The imported GPX layer. May be null if no GPX data.
+         * @param markerLayer The imported marker layer. May be null if no marker.
+         * @param postLayerTask The task to run after GPX and/or marker layer has been added to MapView.
+         */
+        public GpxImporterData(GpxLayer gpxLayer, MarkerLayer markerLayer, Runnable postLayerTask) {
+            this.gpxLayer = gpxLayer;
+            this.markerLayer = markerLayer;
+            this.postLayerTask = postLayerTask;
+        }
+
+        /**
+         * Returns the imported GPX layer. May be null if no GPX data.
+         * @return the imported GPX layer. May be null if no GPX data.
+         */
+        public GpxLayer getGpxLayer() {
+            return gpxLayer;
+        }
+
+        /**
+         * Returns the imported marker layer. May be null if no marker.
+         * @return the imported marker layer. May be null if no marker.
+         */
+        public MarkerLayer getMarkerLayer() {
+            return markerLayer;
+        }
+
+        /**
+         * Returns the task to run after GPX and/or marker layer has been added to MapView.
+         * @return the task to run after GPX and/or marker layer has been added to MapView.
+         */
+        public Runnable getPostLayerTask() {
+            return postLayerTask;
+        }
+    }
+
+    /**
+     * Constructs a new {@code GpxImporter}.
+     */
+    public GpxImporter() {
+        super(getFileFilter());
+    }
+
+    /**
+     * Returns a GPX file filter (*.gpx and *.gpx.gz files).
+     * @return a GPX file filter
+     */
+    public static ExtensionFileFilter getFileFilter() {
+        return ExtensionFileFilter.newFilterWithArchiveExtensions(
+            "gpx", Main.pref.get("save.extension.gpx", "gpx"), tr("GPX Files"), true);
+    }
+
+    @Override
+    public void importData(File file, ProgressMonitor progressMonitor) throws IOException {
+        final String fileName = file.getName();
+
+        try (InputStream is = Compression.getUncompressedFileInputStream(file)) {
+            GpxReader r = new GpxReader(is);
+            boolean parsedProperly = r.parse(true);
+            r.getGpxData().storageFile = file;
+            addLayers(loadLayers(r.getGpxData(), parsedProperly, fileName, tr("Markers from {0}", fileName)));
+        } catch (SAXException e) {
+            Logging.error(e);
+            throw new IOException(tr("Parsing data for layer ''{0}'' failed", fileName), e);
+        }
+    }
+
+    /**
+     * Adds the specified GPX and marker layers to Map.main
+     * @param data The layers to add
+     * @see #loadLayers
+     */
+    public static void addLayers(final GpxImporterData data) {
+        // FIXME: remove UI stuff from the IO subsystem
+        GuiHelper.runInEDT(() -> {
+            if (data.markerLayer != null) {
+                MainApplication.getLayerManager().addLayer(data.markerLayer);
+            }
+            if (data.gpxLayer != null) {
+                MainApplication.getLayerManager().addLayer(data.gpxLayer);
+            }
+            data.postLayerTask.run();
+        });
+    }
+
+    /**
+     * Replies the new GPX and marker layers corresponding to the specified GPX data.
+     * @param data The GPX data
+     * @param parsedProperly True if GPX data has been properly parsed by {@link GpxReader#parse}
+     * @param gpxLayerName The GPX layer name
+     * @param markerLayerName The marker layer name
+     * @return the new GPX and marker layers corresponding to the specified GPX data, to be used with {@link #addLayers}
+     * @see #addLayers
+     */
+    public static GpxImporterData loadLayers(final GpxData data, final boolean parsedProperly,
+            final String gpxLayerName, String markerLayerName) {
+        GpxLayer gpxLayer = null;
+        MarkerLayer markerLayer = null;
+        if (data.hasRoutePoints() || data.hasTrackPoints()) {
+            gpxLayer = new GpxLayer(data, gpxLayerName, data.storageFile != null);
+        }
+        if (Main.pref.getBoolean("marker.makeautomarkers", true) && !data.waypoints.isEmpty()) {
+            markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer);
+            if (markerLayer.data.isEmpty()) {
+                markerLayer = null;
+            }
+        }
+        Runnable postLayerTask = () -> {
+            if (!parsedProperly) {
+                String msg;
+                if (data.storageFile == null) {
+                    msg = tr("Error occurred while parsing gpx data for layer ''{0}''. Only a part of the file will be available.",
+                            gpxLayerName);
+                } else {
+                    msg = tr("Error occurred while parsing gpx file ''{0}''. Only a part of the file will be available.",
+                            data.storageFile.getPath());
+                }
+                JOptionPane.showMessageDialog(null, msg);
+            }
+        };
+        return new GpxImporterData(gpxLayer, markerLayer, postLayerTask);
+    }
+
+    /**
+     * Replies the new GPX and marker layers corresponding to the specified GPX file.
+     * @param is input stream to GPX data
+     * @param associatedFile GPX file
+     * @param gpxLayerName The GPX layer name
+     * @param markerLayerName The marker layer name
+     * @param progressMonitor The progress monitor
+     * @return the new GPX and marker layers corresponding to the specified GPX file
+     * @throws IOException if an I/O error occurs
+     */
+    public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
+            final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException {
+        try {
+            final GpxReader r = new GpxReader(is);
+            final boolean parsedProperly = r.parse(true);
+            r.getGpxData().storageFile = associatedFile;
+            return loadLayers(r.getGpxData(), parsedProperly, gpxLayerName, markerLayerName);
+        } catch (SAXException e) {
+            Logging.error(e);
+            throw new IOException(tr("Parsing data for layer ''{0}'' failed", gpxLayerName), e);
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/JpgImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/JpgImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/JpgImporter.java	(revision 12671)
@@ -0,0 +1,132 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.IllegalDataException;
+
+/**
+ * File importer allowing to import geottaged images (*.jpg files).
+ *
+ */
+public class JpgImporter extends FileImporter {
+    private GpxLayer gpx;
+
+    /**
+     * The default file filter (only *.jpg files).
+     */
+    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
+            "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg)");
+
+    /**
+     * An alternate file filter that also includes folders.
+     * @since 5438
+     */
+    public static final ExtensionFileFilter FILE_FILTER_WITH_FOLDERS = new ExtensionFileFilter(
+            "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg, "+ tr("folder")+')');
+
+    /**
+     * Constructs a new {@code JpgImporter}.
+     */
+    public JpgImporter() {
+        this(false);
+    }
+
+    /**
+     * Constructs a new {@code JpgImporter} with folders selection, if wanted.
+     * @param includeFolders If true, includes folders in the file filter
+     * @since 5438
+     */
+    public JpgImporter(boolean includeFolders) {
+        super(includeFolders ? FILE_FILTER_WITH_FOLDERS : FILE_FILTER);
+    }
+
+    /**
+     * Constructs a new {@code JpgImporter} for the given GPX layer. Folders selection is allowed.
+     * @param gpx The GPX layer
+     */
+    public JpgImporter(GpxLayer gpx) {
+        this(true);
+        this.gpx = gpx;
+    }
+
+    @Override
+    public boolean acceptFile(File pathname) {
+        return super.acceptFile(pathname) || pathname.isDirectory();
+    }
+
+    @Override
+    public void importData(List<File> sel, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
+        progressMonitor.beginTask(tr("Looking for image files"), 1);
+        try {
+            List<File> files = new ArrayList<>();
+            Set<String> visitedDirs = new HashSet<>();
+            addRecursiveFiles(files, visitedDirs, sel, progressMonitor.createSubTaskMonitor(1, true));
+
+            if (progressMonitor.isCanceled())
+                return;
+
+            if (files.isEmpty())
+                throw new IOException(tr("No image files found."));
+
+            GeoImageLayer.create(files, gpx);
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+
+    static void addRecursiveFiles(List<File> files, Set<String> visitedDirs, List<File> sel, ProgressMonitor progressMonitor)
+            throws IOException {
+
+        if (progressMonitor.isCanceled())
+            return;
+
+        progressMonitor.beginTask(null, sel.size());
+        try {
+            for (File f : sel) {
+                if (f.isDirectory()) {
+                    if (visitedDirs.add(f.getCanonicalPath())) { // Do not loop over symlinks
+                        File[] dirFiles = f.listFiles(); // Can be null for some strange directories (like lost+found)
+                        if (dirFiles != null) {
+                            addRecursiveFiles(files, visitedDirs, Arrays.asList(dirFiles), progressMonitor.createSubTaskMonitor(1, true));
+                        }
+                    } else {
+                        progressMonitor.worked(1);
+                    }
+                } else {
+                    if (FILE_FILTER.accept(f)) {
+                        files.add(f);
+                    }
+                    progressMonitor.worked(1);
+                }
+            }
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+
+    @Override
+    public boolean isBatchImporter() {
+        return true;
+    }
+
+    /**
+     * Needs to be the last, to avoid problems.
+     */
+    @Override
+    public double getPriority() {
+        return -1000;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/NMEAImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/NMEAImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/NMEAImporter.java	(revision 12671)
@@ -0,0 +1,109 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.Notification;
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter.GpxImporterData;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.nmea.NmeaReader;
+
+/**
+ * File importer allowing to import NMEA-0183 files (*.nmea/nme/nma/log/txt files).
+ * @since 1637
+ */
+public class NMEAImporter extends FileImporter {
+
+    /**
+     * The NMEA file filter (*.nmea *.nme *.nma *.log *.txt files).
+     */
+    public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
+            "nmea,nme,nma,log,txt", "nmea", tr("NMEA-0183 Files"), false);
+
+    /**
+     * Constructs a new {@code NMEAImporter}.
+     */
+    public NMEAImporter() {
+        super(FILE_FILTER);
+    }
+
+    @Override
+    public void importData(File file, ProgressMonitor progressMonitor) throws IOException {
+        final String fn = file.getName();
+        try (InputStream fis = Compression.getUncompressedFileInputStream(file)) {
+            final NmeaReader r = new NmeaReader(fis);
+            if (r.getNumberOfCoordinates() > 0) {
+                r.data.storageFile = file;
+                final GpxLayer gpxLayer = new GpxLayer(r.data, fn, true);
+                final File fileFinal = file;
+
+                GuiHelper.runInEDT(() -> {
+                    MainApplication.getLayerManager().addLayer(gpxLayer);
+                    if (Main.pref.getBoolean("marker.makeautomarkers", true)) {
+                        MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), fileFinal, gpxLayer);
+                        if (!ml.data.isEmpty()) {
+                            MainApplication.getLayerManager().addLayer(ml);
+                        }
+                    }
+                });
+            }
+            showNmeaInfobox(r.getNumberOfCoordinates() > 0, r);
+        }
+    }
+
+    private static void showNmeaInfobox(boolean success, NmeaReader r) {
+        final StringBuilder msg = new StringBuilder(160).append("<html>")
+           .append(tr("Coordinates imported: {0}", r.getNumberOfCoordinates())).append("<br>")
+           .append(tr("Malformed sentences: {0}", r.getParserMalformed())).append("<br>")
+           .append(tr("Checksum errors: {0}", r.getParserChecksumErrors())).append("<br>");
+        if (!success) {
+            msg.append(tr("Unknown sentences: {0}", r.getParserUnknown())).append("<br>");
+        }
+        msg.append(tr("Zero coordinates: {0}", r.getParserZeroCoordinates()))
+           .append("</html>");
+        if (success) {
+            SwingUtilities.invokeLater(() -> new Notification(
+                    "<h3>" + tr("NMEA import success:") + "</h3>" + msg.toString())
+                    .setIcon(JOptionPane.INFORMATION_MESSAGE)
+                    .show());
+        } else {
+            HelpAwareOptionPane.showMessageDialogInEDT(
+                    Main.parent,
+                    msg.toString(),
+                    tr("NMEA import failure!"),
+                    JOptionPane.ERROR_MESSAGE, null);
+        }
+    }
+
+    /**
+     * Replies the new GPX and marker layers corresponding to the specified NMEA file.
+     * @param is input stream to NMEA 0183 data
+     * @param associatedFile NMEA file
+     * @param gpxLayerName The GPX layer name
+     * @param markerLayerName The marker layer name
+     * @return the new GPX and marker layers corresponding to the specified NMEA file
+     * @throws IOException if an I/O error occurs
+     */
+    public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
+            final String gpxLayerName, String markerLayerName) throws IOException {
+        final NmeaReader r = new NmeaReader(is);
+        final boolean parsedProperly = r.getNumberOfCoordinates() > 0;
+        r.data.storageFile = associatedFile;
+        return GpxImporter.loadLayers(r.data, parsedProperly, gpxLayerName, markerLayerName);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/NoteExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/NoteExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/NoteExporter.java	(revision 12671)
@@ -0,0 +1,48 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.NoteLayer;
+import org.openstreetmap.josm.io.NoteWriter;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Exporter to write note data to an XML file
+ */
+public class NoteExporter extends FileExporter {
+
+    /** File extension filter for .osn files */
+    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
+            "osn", "osn", tr("Note Files") + " (*.osn)");
+
+    /** Create a new note exporter with the default .osn file filter */
+    public NoteExporter() {
+        super(FILE_FILTER);
+    }
+
+    @Override
+    public boolean acceptFile(File pathname, Layer layer) {
+        if (!(layer instanceof NoteLayer))
+            return false;
+        return super.acceptFile(pathname, layer);
+    }
+
+    @Override
+    public void exportData(File file, Layer layer) throws IOException {
+        Logging.info("exporting notes to file: " + file);
+        if (layer instanceof NoteLayer) {
+            try (OutputStream os = new FileOutputStream(file);
+                 NoteWriter writer = new NoteWriter(os)) {
+                writer.write(((NoteLayer) layer).getNoteData());
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/NoteImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/NoteImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/NoteImporter.java	(revision 12671)
@@ -0,0 +1,79 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.notes.Note;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.NoteLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.NoteReader;
+import org.openstreetmap.josm.tools.Logging;
+import org.xml.sax.SAXException;
+
+/**
+ * File importer that reads note dump files (*.osn, .osn.gz and .osn.bz2)
+ * @since 7538
+ */
+public class NoteImporter extends FileImporter {
+
+    private static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
+            "osn", "osn", tr("Note Files"), true);
+
+    /** Create an importer for note dump files */
+    public NoteImporter() {
+        super(FILE_FILTER);
+    }
+
+    @Override
+    public void importData(final File file, ProgressMonitor progressMonitor) throws IOException {
+        if (Logging.isDebugEnabled()) {
+            Logging.debug("importing notes file {0}", file.getAbsolutePath());
+        }
+        try (InputStream is = Compression.getUncompressedFileInputStream(file)) {
+            final NoteLayer layer = loadLayer(is, file, file.getName(), progressMonitor);
+            if (!MainApplication.getLayerManager().containsLayer(layer)) {
+                MainApplication.getLayerManager().addLayer(layer);
+            }
+        } catch (SAXException e) {
+            Logging.error("error opening up notes file");
+            Logging.error(e);
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Load note layer from InputStream.
+     * @param in input stream
+     * @param associatedFile filename of data (can be <code>null</code> if the stream does not come from a file)
+     * @param layerName name of generated layer
+     * @param progressMonitor handler for progress monitoring and canceling
+     * @return note layer
+     * @throws IOException if any I/O error occurs
+     * @throws SAXException if any SAX error occurs
+     * @since 9746
+     */
+    public NoteLayer loadLayer(InputStream in, final File associatedFile, final String layerName, ProgressMonitor progressMonitor)
+            throws SAXException, IOException {
+        final List<Note> fileNotes = new NoteReader(in).parse();
+        List<NoteLayer> noteLayers = null;
+        if (MainApplication.getMap() != null) {
+            noteLayers = MainApplication.getLayerManager().getLayersOfType(NoteLayer.class);
+        }
+        final NoteLayer layer;
+        if (noteLayers != null && !noteLayers.isEmpty()) {
+            layer = noteLayers.get(0);
+            layer.getNoteData().addNotes(fileNotes);
+        } else {
+            layer = new NoteLayer(fileNotes, associatedFile != null ? associatedFile.getName() : tr("Notes"));
+        }
+        return layer;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmBzip2Exporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmBzip2Exporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmBzip2Exporter.java	(revision 12671)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+
+/**
+ * OSM data exporter that compresses it in Bzip2 format.
+ */
+public class OsmBzip2Exporter extends OsmExporter {
+
+    /**
+     * Constructs a new {@code OsmBzip2Exporter}.
+     */
+    public OsmBzip2Exporter() {
+        super(new ExtensionFileFilter(
+            "osm.bz2,osm.bz", "osm.bz2", tr("OSM Server Files bzip2 compressed") + " (*.osm.bz2, *.osm.bz)"));
+    }
+
+    // compression handling is performed in super-class
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmChangeImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmChangeImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmChangeImporter.java	(revision 12671)
@@ -0,0 +1,76 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.io.OsmChangeReader;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * File importer that reads OSM change files (*.osc).
+ * @see <a href="http://wiki.openstreetmap.org/wiki/OsmChange">OsmChange</a>
+ */
+public class OsmChangeImporter extends FileImporter {
+
+    public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
+            "osc", "osc", tr("OsmChange File"), true);
+
+    /**
+     * Constructs a new {@code OsmChangeImporter}.
+     */
+    public OsmChangeImporter() {
+        super(FILE_FILTER);
+    }
+
+    public OsmChangeImporter(ExtensionFileFilter filter) {
+        super(filter);
+    }
+
+    @Override
+    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
+        try {
+            importData(Compression.getUncompressedFileInputStream(file), file, progressMonitor);
+        } catch (FileNotFoundException e) {
+            Logging.error(e);
+            throw new IOException(tr("File ''{0}'' does not exist.", file.getName()), e);
+        }
+    }
+
+    protected void importData(InputStream in, final File associatedFile, ProgressMonitor progressMonitor) throws IllegalDataException {
+        final DataSet dataSet = OsmChangeReader.parseDataSet(in, progressMonitor);
+        final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
+        addDataLayer(dataSet, layer, associatedFile.getPath());
+    }
+
+    protected void addDataLayer(final DataSet dataSet, final OsmDataLayer layer, final String filePath) {
+        // FIXME: remove UI stuff from IO subsystem
+        //
+        GuiHelper.runInEDT(() -> {
+            if (dataSet.allPrimitives().isEmpty()) {
+                JOptionPane.showMessageDialog(
+                        Main.parent,
+                        tr("No data found in file {0}.", filePath),
+                        tr("Open OsmChange file"),
+                        JOptionPane.INFORMATION_MESSAGE);
+            }
+            MainApplication.getLayerManager().addLayer(layer);
+            layer.onPostLoadFromFile();
+        });
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmExporter.java	(revision 12671)
@@ -0,0 +1,139 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.OsmWriter;
+import org.openstreetmap.josm.io.OsmWriterFactory;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Exports data to an .osm file.
+ * @since 1949
+ */
+public class OsmExporter extends FileExporter {
+
+    /**
+     * Constructs a new {@code OsmExporter}.
+     */
+    public OsmExporter() {
+        super(new ExtensionFileFilter(
+            "osm,xml", "osm", tr("OSM Server Files") + " (*.osm)"));
+    }
+
+    /**
+     * Constructs a new {@code OsmExporter}.
+     * @param filter The extension file filter
+     */
+    public OsmExporter(ExtensionFileFilter filter) {
+        super(filter);
+    }
+
+    @Override
+    public boolean acceptFile(File pathname, Layer layer) {
+        if (!(layer instanceof OsmDataLayer))
+            return false;
+        return super.acceptFile(pathname, layer);
+    }
+
+    @Override
+    public void exportData(File file, Layer layer) throws IOException {
+        exportData(file, layer, false);
+    }
+
+    /**
+     * Exports OSM data to the given file.
+     * @param file Output file
+     * @param layer Data layer. Must be an instance of {@link OsmDataLayer}.
+     * @param noBackup if {@code true}, the potential backup file created if the output file already exists will be deleted
+     *                 after a successful export
+     * @throws IllegalArgumentException if {@code layer} is not an instance of {@code OsmDataLayer}
+     */
+    public void exportData(File file, Layer layer, boolean noBackup) {
+        if (!(layer instanceof OsmDataLayer)) {
+            throw new IllegalArgumentException(
+                    MessageFormat.format("Expected instance of OsmDataLayer. Got ''{0}''.", layer.getClass().getName()));
+        }
+        save(file, (OsmDataLayer) layer, noBackup);
+    }
+
+    protected static OutputStream getOutputStream(File file) throws IOException {
+        return Compression.getCompressedFileOutputStream(file);
+    }
+
+    private void save(File file, OsmDataLayer layer, boolean noBackup) {
+        File tmpFile = null;
+        try {
+            // use a tmp file because if something errors out in the process of writing the file,
+            // we might just end up with a truncated file.  That can destroy lots of work.
+            if (file.exists()) {
+                tmpFile = new File(file.getPath() + '~');
+                Utils.copyFile(file, tmpFile);
+            }
+
+            doSave(file, layer);
+            if ((noBackup || !Main.pref.getBoolean("save.keepbackup", false)) && tmpFile != null) {
+                Utils.deleteFile(tmpFile);
+            }
+            layer.onPostSaveToFile();
+        } catch (IOException e) {
+            Logging.error(e);
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("<html>An error occurred while saving.<br>Error is:<br>{0}</html>",
+                            Utils.escapeReservedCharactersHTML(e.getMessage())),
+                    tr("Error"),
+                    JOptionPane.ERROR_MESSAGE
+            );
+
+            try {
+                // if the file save failed, then the tempfile will not be deleted. So, restore the backup if we made one.
+                if (tmpFile != null && tmpFile.exists()) {
+                    Utils.copyFile(tmpFile, file);
+                }
+            } catch (IOException e2) {
+                Logging.error(e2);
+                JOptionPane.showMessageDialog(
+                        Main.parent,
+                        tr("<html>An error occurred while restoring backup file.<br>Error is:<br>{0}</html>",
+                                Utils.escapeReservedCharactersHTML(e2.getMessage())),
+                        tr("Error"),
+                        JOptionPane.ERROR_MESSAGE
+                );
+            }
+        }
+    }
+
+    protected void doSave(File file, OsmDataLayer layer) throws IOException {
+        // create outputstream and wrap it with gzip or bzip, if necessary
+        try (
+            OutputStream out = getOutputStream(file);
+            Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
+            OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, layer.data.getVersion())
+        ) {
+            layer.data.getReadLock().lock();
+            try {
+                w.writeLayer(layer);
+            } finally {
+                layer.data.getReadLock().unlock();
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmGzipExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmGzipExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmGzipExporter.java	(revision 12671)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+
+/**
+ * OSM data exporter that compresses it in GZip format.
+ */
+public class OsmGzipExporter extends OsmExporter {
+
+    /**
+     * Constructs a new {@code OsmGzipExporter}.
+     */
+    public OsmGzipExporter() {
+        super(new ExtensionFileFilter(
+            "osm.gz", "osm.gz", tr("OSM Server Files gzip compressed") + " (*.osm.gz)"));
+    }
+
+    // compression handling is performed in super-class
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmImporter.java	(revision 12671)
@@ -0,0 +1,156 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.io.OsmReader;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * File importer that reads *.osm data files. (main storage format for OSM data in JOSM)
+ */
+public class OsmImporter extends FileImporter {
+
+    /**
+     * The OSM file filter (*.osm and *.xml files).
+     */
+    public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
+            "osm,xml", "osm", tr("OSM Server Files") + " (*.osm, *.osm.gz, *.osm.bz2, *.osm.zip, *.xml)",
+            ExtensionFileFilter.AddArchiveExtension.NONE, Arrays.asList("gz", "bz", "bz2", "zip"));
+
+    /**
+     * Utility class containing imported OSM layer, and a task to run after it is added to MapView.
+     */
+    public static class OsmImporterData {
+
+        private final OsmDataLayer layer;
+        private final Runnable postLayerTask;
+
+        public OsmImporterData(OsmDataLayer layer, Runnable postLayerTask) {
+            this.layer = layer;
+            this.postLayerTask = postLayerTask;
+        }
+
+        public OsmDataLayer getLayer() {
+            return layer;
+        }
+
+        public Runnable getPostLayerTask() {
+            return postLayerTask;
+        }
+    }
+
+    /**
+     * Constructs a new {@code OsmImporter}.
+     */
+    public OsmImporter() {
+        super(FILE_FILTER);
+    }
+
+    /**
+     * Constructs a new {@code OsmImporter} with the given extension file filter.
+     * @param filter The extension file filter
+     */
+    public OsmImporter(ExtensionFileFilter filter) {
+        super(filter);
+    }
+
+    /**
+     * Imports OSM data from file
+     * @param file file to read data from
+     * @param progressMonitor handler for progress monitoring and canceling
+     */
+    @Override
+    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
+        try (InputStream in = Compression.getUncompressedFileInputStream(file)) {
+            importData(in, file, progressMonitor);
+        } catch (FileNotFoundException e) {
+            Logging.error(e);
+            throw new IOException(tr("File ''{0}'' does not exist.", file.getName()), e);
+        }
+    }
+
+    /**
+     * Imports OSM data from stream
+     * @param in input stream
+     * @param associatedFile filename of data (layer name will be generated from name of file)
+     * @param pm handler for progress monitoring and canceling
+     * @throws IllegalDataException if an error was found while parsing the OSM data
+     */
+    protected void importData(InputStream in, final File associatedFile, ProgressMonitor pm) throws IllegalDataException {
+        final OsmImporterData data = loadLayer(in, associatedFile,
+                associatedFile == null ? OsmDataLayer.createNewName() : associatedFile.getName(), pm);
+
+        // FIXME: remove UI stuff from IO subsystem
+        GuiHelper.runInEDT(() -> {
+            OsmDataLayer layer = data.getLayer();
+            MainApplication.getLayerManager().addLayer(layer);
+            data.getPostLayerTask().run();
+            data.getLayer().onPostLoadFromFile();
+        });
+    }
+
+    /**
+     * Load osm data layer from InputStream.
+     * @param in input stream
+     * @param associatedFile filename of data (can be <code>null</code> if the stream does not come from a file)
+     * @param layerName name of generated layer
+     * @param progressMonitor handler for progress monitoring and canceling
+     * @return Utility class containing imported OSM layer, and a task to run after it is added to MapView
+     * @throws IllegalDataException if an error was found while parsing the OSM data
+     */
+    public OsmImporterData loadLayer(InputStream in, final File associatedFile, final String layerName, ProgressMonitor progressMonitor)
+            throws IllegalDataException {
+        final DataSet dataSet = parseDataSet(in, progressMonitor);
+        if (dataSet == null) {
+            throw new IllegalDataException(tr("Invalid dataset"));
+        }
+        OsmDataLayer layer = createLayer(dataSet, associatedFile, layerName);
+        Runnable postLayerTask = createPostLayerTask(dataSet, associatedFile, layerName, layer);
+        return new OsmImporterData(layer, postLayerTask);
+    }
+
+    protected DataSet parseDataSet(InputStream in, ProgressMonitor progressMonitor) throws IllegalDataException {
+        return OsmReader.parseDataSet(in, progressMonitor);
+    }
+
+    protected OsmDataLayer createLayer(final DataSet dataSet, final File associatedFile, final String layerName) {
+        return new OsmDataLayer(dataSet, layerName, associatedFile);
+    }
+
+    protected Runnable createPostLayerTask(final DataSet dataSet, final File associatedFile, final String layerName, final OsmDataLayer layer) {
+        return () -> {
+            if (dataSet.allPrimitives().isEmpty()) {
+                String msg;
+                if (associatedFile == null) {
+                    msg = tr("No data found for layer ''{0}''.", layerName);
+                } else {
+                    msg = tr("No data found in file ''{0}''.", associatedFile.getPath());
+                }
+                JOptionPane.showMessageDialog(
+                        Main.parent,
+                        msg,
+                        tr("Open OSM file"),
+                        JOptionPane.INFORMATION_MESSAGE);
+            }
+            layer.onPostLoadFromFile();
+        };
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/ValidatorErrorExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/ValidatorErrorExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/ValidatorErrorExporter.java	(revision 12671)
@@ -0,0 +1,52 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.ValidatorLayer;
+import org.openstreetmap.josm.io.ValidatorErrorWriter;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Exporter to write validator errors to an XML file.
+ * @since 12667
+ */
+public class ValidatorErrorExporter extends FileExporter {
+
+    /** File extension filter for .xml files */
+    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
+            "xml", "xml", tr("Validation Error Files") + " (*.xml)");
+
+    /** Create a new validator error exporter with the default .xml file filter */
+    public ValidatorErrorExporter() {
+        super(FILE_FILTER);
+    }
+
+    @Override
+    public boolean acceptFile(File pathname, Layer layer) {
+        if (!(layer instanceof ValidatorLayer))
+            return false;
+        return super.acceptFile(pathname, layer);
+    }
+
+    @Override
+    public void exportData(File file, Layer layer) throws IOException {
+        OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
+        if (layer instanceof ValidatorLayer && editLayer != null) {
+            Logging.info("exporting validation errors to file: " + file);
+            try (OutputStream os = new FileOutputStream(file);
+                 ValidatorErrorWriter writer = new ValidatorErrorWriter(os)) {
+                writer.write(editLayer.validationErrors);
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/WMSLayerExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/WMSLayerExporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/WMSLayerExporter.java	(revision 12671)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * Export a WMS layer to a serialized binary file that can be imported later via {@link WMSLayerImporter}.
+ *
+ * @since 5457
+ */
+public class WMSLayerExporter extends FileExporter {
+
+    /** Which version of the file we export */
+    public static final int CURRENT_FILE_VERSION = 6;
+
+    /**
+     * Constructs a new {@code WMSLayerExporter}
+     */
+    public WMSLayerExporter() {
+        super(WMSLayerImporter.FILE_FILTER);
+    }
+
+    @Override
+    public void exportData(File file, Layer layer) throws IOException {
+        CheckParameterUtil.ensureParameterNotNull(file, "file");
+        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
+
+        if (layer instanceof AbstractTileSourceLayer) {
+            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
+                oos.writeInt(CURRENT_FILE_VERSION); // file version
+                oos.writeObject(MainApplication.getMap().mapView.getCenter());
+                ImageryPreferenceEntry entry = new ImageryPreferenceEntry(((AbstractTileSourceLayer) layer).getInfo());
+                oos.writeObject(Preferences.serializeStruct(entry, ImageryPreferenceEntry.class));
+            }
+        }
+    }
+
+    @Override
+    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
+        setEnabled(e.getSource().getActiveLayer() instanceof AbstractTileSourceLayer);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/WMSLayerImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/WMSLayerImporter.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/WMSLayerImporter.java	(revision 12671)
@@ -0,0 +1,95 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.util.Map;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.imagery.ImageryInfo;
+import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.ImageryLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * Import a WMS layer from a serialized binary file previously exported via {@link WMSLayerExporter}.
+ * @since 5457
+ */
+public class WMSLayerImporter extends FileImporter {
+
+    /**
+     * The file filter used in "open" and "save" dialogs for WMS layers.
+     */
+    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
+            "wms", "wms", tr("WMS Files (*.wms)"));
+
+    /**
+     * Constructs a new {@code WMSLayerImporter}.
+     */
+    public WMSLayerImporter() {
+        super(FILE_FILTER);
+    }
+
+    @Override
+    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
+        CheckParameterUtil.ensureParameterNotNull(file, "file");
+        final EastNorth zoomTo;
+        ImageryInfo info = null;
+        final ImageryLayer layer;
+
+        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
+            int sfv = ois.readInt();
+            if (sfv < 5) {
+                throw new InvalidClassException(tr("Unsupported WMS file version; found {0}, expected {1}", sfv, 5));
+            } else if (sfv == 5) {
+                ois.readInt(); // dax - not needed
+                ois.readInt(); // day - not needed
+                zoomTo = null;
+
+                int imageSize = ois.readInt();
+                double pixelPerDegree = ois.readDouble();
+
+                String name = (String) ois.readObject();
+                String extendedUrl = (String) ois.readObject();
+
+                info = new ImageryInfo(name);
+                info.setExtendedUrl(extendedUrl);
+                info.setPixelPerDegree(pixelPerDegree);
+                info.setTileSize(imageSize);
+            } else if (sfv == WMSLayerExporter.CURRENT_FILE_VERSION) {
+                zoomTo = (EastNorth) ois.readObject();
+
+                @SuppressWarnings("unchecked")
+                ImageryPreferenceEntry entry = Preferences.deserializeStruct(
+                        (Map<String, String>) ois.readObject(),
+                        ImageryPreferenceEntry.class);
+                info = new ImageryInfo(entry);
+            } else {
+                throw new InvalidClassException(tr("Unsupported WMS file version; found {0}, expected {1}", sfv, 6));
+            }
+        } catch (ClassNotFoundException e) {
+            throw new IllegalDataException(e);
+        }
+        layer = ImageryLayer.create(info);
+
+
+        // FIXME: remove UI stuff from IO subsystem
+        GuiHelper.runInEDT(() -> {
+            MainApplication.getLayerManager().addLayer(layer);
+            if (zoomTo != null) {
+                MainApplication.getMap().mapView.zoomTo(zoomTo);
+            }
+        });
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/importexport/package-info.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/importexport/package-info.java	(revision 12671)
+++ trunk/src/org/openstreetmap/josm/gui/io/importexport/package-info.java	(revision 12671)
@@ -0,0 +1,6 @@
+// License: GPL. For details, see LICENSE file.
+
+/**
+ * Provides GUI classes for handling file import/export operations.
+ */
+package org.openstreetmap.josm.gui.io.importexport;
Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 12671)
@@ -95,4 +95,5 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.io.importexport.WMSLayerImporter;
 import org.openstreetmap.josm.gui.layer.imagery.AutoLoadTilesAction;
 import org.openstreetmap.josm.gui.layer.imagery.AutoZoomAction;
@@ -115,5 +116,4 @@
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.io.WMSLayerImporter;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Logging;
Index: trunk/src/org/openstreetmap/josm/gui/layer/AutosaveTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AutosaveTask.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AutosaveTask.java	(revision 12671)
@@ -39,4 +39,6 @@
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.Notification;
+import org.openstreetmap.josm.gui.io.importexport.OsmExporter;
+import org.openstreetmap.josm.gui.io.importexport.OsmImporter;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
@@ -44,6 +46,4 @@
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.io.OsmExporter;
-import org.openstreetmap.josm.io.OsmImporter;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
Index: trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 12671)
@@ -31,4 +31,5 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
 import org.openstreetmap.josm.gui.layer.gpx.ChooseTrackVisibilityAction;
 import org.openstreetmap.josm.gui.layer.gpx.ConvertToDataLayerAction;
@@ -41,5 +42,4 @@
 import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction;
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
-import org.openstreetmap.josm.io.GpxImporter;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.date.DateUtils;
Index: trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(revision 12671)
@@ -39,6 +39,6 @@
 import org.openstreetmap.josm.gui.io.AbstractIOTask;
 import org.openstreetmap.josm.gui.io.UploadNoteLayerTask;
+import org.openstreetmap.josm.gui.io.importexport.NoteExporter;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.io.NoteExporter;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.XmlWriter;
Index: trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 12671)
@@ -97,4 +97,5 @@
 import org.openstreetmap.josm.gui.io.UploadDialog;
 import org.openstreetmap.josm.gui.io.UploadLayerTask;
+import org.openstreetmap.josm.gui.io.importexport.OsmImporter;
 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
@@ -103,5 +104,4 @@
 import org.openstreetmap.josm.gui.widgets.FileChooserManager;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
-import org.openstreetmap.josm.io.OsmImporter;
 import org.openstreetmap.josm.tools.AlphanumComparator;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
Index: trunk/src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java	(revision 12671)
@@ -27,9 +27,9 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.io.importexport.ValidatorErrorExporter;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
-import org.openstreetmap.josm.io.ValidatorErrorExporter;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.MultiMap;
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 12671)
@@ -72,4 +72,5 @@
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.io.importexport.JpgImporter;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
@@ -78,5 +79,4 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.io.GpxReader;
-import org.openstreetmap.josm.io.JpgImporter;
 import org.openstreetmap.josm.tools.ExifReader;
 import org.openstreetmap.josm.tools.GBC;
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 12671)
@@ -56,4 +56,5 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.io.importexport.JpgImporter;
 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
@@ -67,5 +68,4 @@
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.io.JpgImporter;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportImagesAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportImagesAction.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportImagesAction.java	(revision 12671)
@@ -16,9 +16,9 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.io.importexport.JpgImporter;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
 import org.openstreetmap.josm.gui.widgets.FileChooserManager;
-import org.openstreetmap.josm.io.JpgImporter;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Utils;
Index: trunk/src/org/openstreetmap/josm/io/AllFormatsImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/AllFormatsImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,45 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.util.Iterator;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-
-/**
- * Dummy importer that adds the "All Formats"-Filter when opening files
- */
-public class AllFormatsImporter extends FileImporter {
-    /**
-     * Constructs a new {@code AllFormatsImporter}.
-     */
-    public AllFormatsImporter() {
-        super(new ExtensionFileFilter(getAllExtensions(), "", tr("All Formats")
-                + " (*.gpx *.osm *.nmea *.jpg ...)"));
-    }
-
-    @Override
-    public boolean acceptFile(File pathname) {
-        return false;
-    }
-
-    /**
-     * Builds list of all supported extensions by the registered FileImporters.
-     * @return String comma separated list of supported file extensions
-     */
-    private static String getAllExtensions() {
-        Iterator<FileImporter> imp = ExtensionFileFilter.getImporters().iterator();
-        StringBuilder ext = new StringBuilder();
-        while (imp.hasNext()) {
-            FileImporter fi = imp.next();
-            if (fi instanceof AllFormatsImporter) {
-                continue;
-            }
-            ext.append(fi.filter.getExtensions()).append(',');
-        }
-        // remove last comma
-        return ext.substring(0, ext.length()-1);
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/FileExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/FileExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,96 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
-import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
-
-/**
- * Abstract base class for file exporters - IO classes that save layers to a file.
- */
-public abstract class FileExporter implements ActiveLayerChangeListener {
-
-    public final ExtensionFileFilter filter;
-
-    private boolean enabled;
-    private boolean canceled;
-
-    /**
-     * Constructs a new {@code FileExporter}.
-     * @param filter The extension file filter
-     */
-    public FileExporter(ExtensionFileFilter filter) {
-        this.filter = filter;
-        this.enabled = true;
-    }
-
-    /**
-     * Check if this exporter can export a certain layer to a certain file.
-     *
-     * Most exporters support just a single layer type.
-     * @param pathname the target file name (check file extension using the {@link #filter}
-     * @param layer the layer requested for export
-     * @return true, if the exporter can handle the layer and filename is okay
-     */
-    public boolean acceptFile(File pathname, Layer layer) {
-        return filter.acceptName(pathname.getName());
-    }
-
-    /**
-     * Execute the data export. (To be overridden by subclasses.)
-     *
-     * @param file target file
-     * @param layer the layer to export
-     * @throws IOException in case of an IO error
-     */
-    public void exportData(File file, Layer layer) throws IOException {
-        throw new IOException(tr("Could not export ''{0}''.", file.getName()));
-    }
-
-    /**
-     * Returns the enabled state of this {@code FileExporter}. When enabled, it is listed and usable in "File-&gt;Save" dialogs.
-     * @return true if this {@code FileExporter} is enabled
-     * @since 5459
-     */
-    public final boolean isEnabled() {
-        return enabled;
-    }
-
-    /**
-     * Sets the enabled state of the {@code FileExporter}. When enabled, it is listed and usable in "File-&gt;Save" dialogs.
-     * @param enabled true to enable this {@code FileExporter}, false to disable it
-     * @since 5459
-     */
-    public final void setEnabled(boolean enabled) {
-        this.enabled = enabled;
-    }
-
-    @Override
-    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
-        // To be overriden by subclasses if their enabled state depends of the active layer nature
-    }
-
-    /**
-     * Determines if this exporter has been canceled during export.
-     * @return true if this {@code FileExporter} has been canceled
-     * @since 6815
-     */
-    public final boolean isCanceled() {
-        return canceled;
-    }
-
-    /**
-     * Marks this exporter as canceled.
-     * @param canceled true to mark this exporter as canceled, {@code false} otherwise
-     * @since 6815
-     */
-    public final void setCanceled(boolean canceled) {
-        this.canceled = canceled;
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/FileImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/FileImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,187 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.Notification;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.Utils;
-import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
-
-/**
- * Abstract file importer.
- * @since 1637
- * @since 10386 (signature)
- */
-public abstract class FileImporter implements Comparable<FileImporter> {
-
-    /**
-     * The extension file filter used to accept files.
-     */
-    public final ExtensionFileFilter filter;
-
-    private boolean enabled;
-
-    /**
-     * Constructs a new {@code FileImporter} with the given extension file filter.
-     * @param filter The extension file filter
-     */
-    public FileImporter(ExtensionFileFilter filter) {
-        this.filter = filter;
-        this.enabled = true;
-    }
-
-    /**
-     * Determines if this file importer accepts the given file.
-     * @param pathname The file to test
-     * @return {@code true} if this file importer accepts the given file, {@code false} otherwise
-     */
-    public boolean acceptFile(File pathname) {
-        return filter.acceptName(pathname.getName());
-    }
-
-    /**
-     * A batch importer is a file importer that prefers to read multiple files at the same time.
-     * @return {@code true} if this importer is a batch importer
-     */
-    public boolean isBatchImporter() {
-        return false;
-    }
-
-    /**
-     * Needs to be implemented if isBatchImporter() returns false.
-     * @param file file to import
-     * @param progressMonitor progress monitor
-     * @throws IOException if any I/O error occurs
-     * @throws IllegalDataException if invalid data is read
-     */
-    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
-        throw new IOException(tr("Could not import ''{0}''.", file.getName()));
-    }
-
-    /**
-     * Needs to be implemented if isBatchImporter() returns true.
-     * @param files files to import
-     * @param progressMonitor progress monitor
-     * @throws IOException if any I/O error occurs
-     * @throws IllegalDataException if invalid data is read
-     */
-    public void importData(List<File> files, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
-        throw new IOException(tr("Could not import files."));
-    }
-
-    /**
-     * Wrapper to {@link #importData(File, ProgressMonitor)} to give meaningful output if things go wrong.
-     * @param f data file to import
-     * @param progressMonitor progress monitor
-     * @return true if data import was successful
-     */
-    public boolean importDataHandleExceptions(File f, ProgressMonitor progressMonitor) {
-        try {
-            Logging.info("Open file: " + f.getAbsolutePath() + " (" + f.length() + " bytes)");
-            importData(f, progressMonitor);
-            return true;
-        } catch (IllegalDataException e) {
-            Throwable cause = e.getCause();
-            if (cause instanceof ImportCancelException) {
-                displayCancel(cause);
-            } else {
-                displayError(f, e);
-            }
-            return false;
-        } catch (IOException e) {
-            displayError(f, e);
-            return false;
-        } catch (RuntimeException | LinkageError e) { // NOPMD
-            BugReportExceptionHandler.handleException(e);
-            return false;
-        }
-    }
-
-    private static void displayError(File f, Exception e) {
-        Logging.error(e);
-        HelpAwareOptionPane.showMessageDialogInEDT(
-                Main.parent,
-                tr("<html>Could not read file ''{0}''.<br>Error is:<br>{1}</html>",
-                        f.getName(), Utils.escapeReservedCharactersHTML(e.getMessage())),
-                tr("Error"),
-                JOptionPane.ERROR_MESSAGE, null
-        );
-    }
-
-    private static void displayCancel(final Throwable t) {
-        GuiHelper.runInEDTAndWait(() -> {
-            Notification note = new Notification(t.getMessage());
-            note.setIcon(JOptionPane.INFORMATION_MESSAGE);
-            note.setDuration(Notification.TIME_SHORT);
-            note.show();
-        });
-    }
-
-    /**
-     * Wrapper to {@link #importData(List, ProgressMonitor)} to give meaningful output if things go wrong.
-     * @param files data files to import
-     * @param progressMonitor progress monitor
-     * @return true if data import was successful
-     */
-    public boolean importDataHandleExceptions(List<File> files, ProgressMonitor progressMonitor) {
-        try {
-            Logging.info("Open "+files.size()+" files");
-            importData(files, progressMonitor);
-            return true;
-        } catch (IOException | IllegalDataException e) {
-            Logging.error(e);
-            HelpAwareOptionPane.showMessageDialogInEDT(
-                    Main.parent,
-                    tr("<html>Could not read files.<br>Error is:<br>{0}</html>", Utils.escapeReservedCharactersHTML(e.getMessage())),
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE, null
-            );
-            return false;
-        }
-    }
-
-    /**
-     * If multiple files (with multiple file formats) are selected,
-     * they are opened in the order of their priorities.
-     * Highest priority comes first.
-     * @return priority
-     */
-    public double getPriority() {
-        return 0;
-    }
-
-    @Override
-    public int compareTo(FileImporter other) {
-        return Double.compare(this.getPriority(), other.getPriority());
-    }
-
-    /**
-     * Returns the enabled state of this {@code FileImporter}. When enabled, it is listed and usable in "File-&gt;Open" dialog.
-     * @return true if this {@code FileImporter} is enabled
-     * @since 5459
-     */
-    public final boolean isEnabled() {
-        return enabled;
-    }
-
-    /**
-     * Sets the enabled state of the {@code FileImporter}. When enabled, it is listed and usable in "File-&gt;Open" dialog.
-     * @param enabled true to enable this {@code FileImporter}, false to disable it
-     * @since 5459
-     */
-    public final void setEnabled(boolean enabled) {
-        this.enabled = enabled;
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/GeoJSONExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GeoJSONExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,43 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-
-/**
- * Exporter to write map data to a GeoJSON file.
- * @since 4886
- */
-public class GeoJSONExporter extends FileExporter {
-
-    /** File extension filter for .geojson files */
-    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
-            "geojson,json", "geojson", tr("GeoJSON Files") + " (*.geojson *.json)");
-
-    /**
-     * Constructs a new {@code GeoJSONExporter} with WGS84 projection.
-     */
-    public GeoJSONExporter() {
-        super(FILE_FILTER);
-    }
-
-    @Override
-    public void exportData(File file, Layer layer) throws IOException {
-        if (layer instanceof OsmDataLayer) {
-            try (Writer out = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
-                out.write(new GeoJSONWriter((OsmDataLayer) layer).write());
-            }
-        } else {
-            throw new IllegalArgumentException(tr("Layer ''{0}'' not supported", layer.getClass().toString()));
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/GpxExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GpxExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,331 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagLayout;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.MessageFormat;
-import java.time.Year;
-import java.util.Optional;
-
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.ListSelectionModel;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.gpx.GpxConstants;
-import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.GpxLayer;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.widgets.JosmTextArea;
-import org.openstreetmap.josm.gui.widgets.JosmTextField;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.Logging;
-
-/**
- * Exports data to a .gpx file. Data may be native GPX or OSM data which will be converted.
- * @since 1949
- */
-public class GpxExporter extends FileExporter implements GpxConstants {
-
-    private static final String GPL_WARNING = "<html><font color='red' size='-2'>"
-        + tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.") + "</html>";
-
-    private static final String[] LICENSES = {
-            "Creative Commons By-SA",
-            "Open Database License (ODbL)",
-            "public domain",
-            "GNU Lesser Public License (LGPL)",
-            "BSD License (MIT/X11)"};
-
-    private static final String[] URLS = {
-            "https://creativecommons.org/licenses/by-sa/3.0",
-            "http://opendatacommons.org/licenses/odbl/1.0",
-            "public domain",
-            "https://www.gnu.org/copyleft/lesser.html",
-            "http://www.opensource.org/licenses/bsd-license.php"};
-
-    /**
-     * Constructs a new {@code GpxExporter}.
-     */
-    public GpxExporter() {
-        super(GpxImporter.getFileFilter());
-    }
-
-    @Override
-    public boolean acceptFile(File pathname, Layer layer) {
-        if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer))
-            return false;
-        return super.acceptFile(pathname, layer);
-    }
-
-    @Override
-    public void exportData(File file, Layer layer) throws IOException {
-        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
-        if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer))
-            throw new IllegalArgumentException(MessageFormat.format("Expected instance of OsmDataLayer or GpxLayer. Got ''{0}''.", layer
-                    .getClass().getName()));
-        CheckParameterUtil.ensureParameterNotNull(file, "file");
-
-        String fn = file.getPath();
-        if (fn.indexOf('.') == -1) {
-            fn += ".gpx";
-            file = new File(fn);
-        }
-
-        // open the dialog asking for options
-        JPanel p = new JPanel(new GridBagLayout());
-
-        GpxData gpxData;
-        // At this moment, we only need to know the attributes of the GpxData,
-        // conversion of OsmDataLayer (if needed) will be done after the dialog is closed.
-        if (layer instanceof GpxLayer) {
-            gpxData = ((GpxLayer) layer).data;
-        } else {
-            gpxData = new GpxData();
-        }
-
-        p.add(new JLabel(tr("GPS track description")), GBC.eol());
-        JosmTextArea desc = new JosmTextArea(3, 40);
-        desc.setWrapStyleWord(true);
-        desc.setLineWrap(true);
-        desc.setText(gpxData.getString(META_DESC));
-        p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH));
-
-        JCheckBox author = new JCheckBox(tr("Add author information"), Main.pref.getBoolean("lastAddAuthor", true));
-        p.add(author, GBC.eol());
-
-        JLabel nameLabel = new JLabel(tr("Real name"));
-        p.add(nameLabel, GBC.std().insets(10, 0, 5, 0));
-        JosmTextField authorName = new JosmTextField();
-        p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL));
-        nameLabel.setLabelFor(authorName);
-
-        JLabel emailLabel = new JLabel(tr("E-Mail"));
-        p.add(emailLabel, GBC.std().insets(10, 0, 5, 0));
-        JosmTextField email = new JosmTextField();
-        p.add(email, GBC.eol().fill(GBC.HORIZONTAL));
-        emailLabel.setLabelFor(email);
-
-        JLabel copyrightLabel = new JLabel(tr("Copyright (URL)"));
-        p.add(copyrightLabel, GBC.std().insets(10, 0, 5, 0));
-        JosmTextField copyright = new JosmTextField();
-        p.add(copyright, GBC.std().fill(GBC.HORIZONTAL));
-        copyrightLabel.setLabelFor(copyright);
-
-        JButton predefined = new JButton(tr("Predefined"));
-        p.add(predefined, GBC.eol().insets(5, 0, 0, 0));
-
-        JLabel copyrightYearLabel = new JLabel(tr("Copyright year"));
-        p.add(copyrightYearLabel, GBC.std().insets(10, 0, 5, 5));
-        JosmTextField copyrightYear = new JosmTextField("");
-        p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL));
-        copyrightYearLabel.setLabelFor(copyrightYear);
-
-        JLabel warning = new JLabel("<html><font size='-2'>&nbsp;</html");
-        p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15, 0, 0, 0));
-        addDependencies(gpxData, author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel,
-                copyrightLabel, copyrightYearLabel, warning);
-
-        p.add(new JLabel(tr("Keywords")), GBC.eol());
-        JosmTextField keywords = new JosmTextField();
-        keywords.setText(gpxData.getString(META_KEYWORDS));
-        p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL));
-
-        ExtendedDialog ed = new ExtendedDialog(Main.parent,
-                tr("Export options"),
-                tr("Export and Save"), tr("Cancel"))
-            .setButtonIcons("exportgpx", "cancel")
-            .setContent(p);
-
-        if (ed.showDialog().getValue() != 1) {
-            setCanceled(true);
-            return;
-        }
-        setCanceled(false);
-
-        Main.pref.put("lastAddAuthor", author.isSelected());
-        if (!authorName.getText().isEmpty()) {
-            Main.pref.put("lastAuthorName", authorName.getText());
-        }
-        if (!copyright.getText().isEmpty()) {
-            Main.pref.put("lastCopyright", copyright.getText());
-        }
-
-        if (layer instanceof OsmDataLayer) {
-            gpxData = ((OsmDataLayer) layer).toGpxData();
-        } else if (layer instanceof GpxLayer) {
-            gpxData = ((GpxLayer) layer).data;
-        } else {
-            gpxData = OsmDataLayer.toGpxData(MainApplication.getLayerManager().getEditDataSet(), file);
-        }
-
-        // add author and copyright details to the gpx data
-        if (author.isSelected()) {
-            if (!authorName.getText().isEmpty()) {
-                gpxData.put(META_AUTHOR_NAME, authorName.getText());
-                gpxData.put(META_COPYRIGHT_AUTHOR, authorName.getText());
-            }
-            if (!email.getText().isEmpty()) {
-                gpxData.put(META_AUTHOR_EMAIL, email.getText());
-            }
-            if (!copyright.getText().isEmpty()) {
-                gpxData.put(META_COPYRIGHT_LICENSE, copyright.getText());
-            }
-            if (!copyrightYear.getText().isEmpty()) {
-                gpxData.put(META_COPYRIGHT_YEAR, copyrightYear.getText());
-            }
-        }
-
-        // add the description to the gpx data
-        if (!desc.getText().isEmpty()) {
-            gpxData.put(META_DESC, desc.getText());
-        }
-
-        // add keywords to the gpx data
-        if (!keywords.getText().isEmpty()) {
-            gpxData.put(META_KEYWORDS, keywords.getText());
-        }
-
-        try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) {
-            new GpxWriter(fo).write(gpxData);
-            fo.flush();
-        } catch (IOException ex) {
-            Logging.error(ex);
-            JOptionPane.showMessageDialog(Main.parent, tr("Error while exporting {0}:\n{1}", fn, ex.getMessage()),
-                    tr("Error"), JOptionPane.ERROR_MESSAGE);
-        }
-    }
-
-    private static void enableCopyright(final GpxData data, final JosmTextField copyright, final JButton predefined,
-            final JosmTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel,
-            final JLabel warning, boolean enable) {
-        copyright.setEnabled(enable);
-        predefined.setEnabled(enable);
-        copyrightYear.setEnabled(enable);
-        copyrightLabel.setEnabled(enable);
-        copyrightYearLabel.setEnabled(enable);
-        warning.setText(enable ? GPL_WARNING : "<html><font size='-2'>&nbsp;</html");
-
-        if (enable) {
-            if (copyrightYear.getText().isEmpty()) {
-                copyrightYear.setText(Optional.ofNullable(data.getString(META_COPYRIGHT_YEAR)).orElseGet(
-                        () -> Year.now().toString()));
-            }
-            if (copyright.getText().isEmpty()) {
-                copyright.setText(Optional.ofNullable(data.getString(META_COPYRIGHT_LICENSE)).orElseGet(
-                        () -> Main.pref.get("lastCopyright", "https://creativecommons.org/licenses/by-sa/2.5")));
-                copyright.setCaretPosition(0);
-            }
-        } else {
-            copyrightYear.setText("");
-            copyright.setText("");
-        }
-    }
-
-    // CHECKSTYLE.OFF: ParameterNumber
-
-    /**
-     * Add all those listeners to handle the enable state of the fields.
-     * @param data GPX data
-     * @param author Author checkbox
-     * @param authorName Author name textfield
-     * @param email E-mail textfield
-     * @param copyright Copyright textfield
-     * @param predefined Predefined button
-     * @param copyrightYear Copyright year textfield
-     * @param nameLabel Name label
-     * @param emailLabel E-mail label
-     * @param copyrightLabel Copyright label
-     * @param copyrightYearLabel Copyright year label
-     * @param warning Warning label
-     */
-    private static void addDependencies(
-            final GpxData data,
-            final JCheckBox author,
-            final JosmTextField authorName,
-            final JosmTextField email,
-            final JosmTextField copyright,
-            final JButton predefined,
-            final JosmTextField copyrightYear,
-            final JLabel nameLabel,
-            final JLabel emailLabel,
-            final JLabel copyrightLabel,
-            final JLabel copyrightYearLabel,
-            final JLabel warning) {
-
-        // CHECKSTYLE.ON: ParameterNumber
-        ActionListener authorActionListener = e -> {
-            boolean b = author.isSelected();
-            authorName.setEnabled(b);
-            email.setEnabled(b);
-            nameLabel.setEnabled(b);
-            emailLabel.setEnabled(b);
-            if (b) {
-                authorName.setText(Optional.ofNullable(data.getString(META_AUTHOR_NAME)).orElseGet(() -> Main.pref.get("lastAuthorName")));
-                email.setText(Optional.ofNullable(data.getString(META_AUTHOR_EMAIL)).orElseGet(() -> Main.pref.get("lastAuthorEmail")));
-            } else {
-                authorName.setText("");
-                email.setText("");
-            }
-            boolean isAuthorSet = !authorName.getText().isEmpty();
-            GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning,
-                    b && isAuthorSet);
-        };
-        author.addActionListener(authorActionListener);
-
-        KeyAdapter authorNameListener = new KeyAdapter() {
-            @Override public void keyReleased(KeyEvent e) {
-                boolean b = !authorName.getText().isEmpty() && author.isSelected();
-                GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b);
-            }
-        };
-        authorName.addKeyListener(authorNameListener);
-
-        predefined.addActionListener(e -> {
-            JList<String> l = new JList<>(LICENSES);
-            l.setVisibleRowCount(LICENSES.length);
-            l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
-            int answer = JOptionPane.showConfirmDialog(
-                    Main.parent,
-                    new JScrollPane(l),
-                    tr("Choose a predefined license"),
-                    JOptionPane.OK_CANCEL_OPTION,
-                    JOptionPane.QUESTION_MESSAGE
-            );
-            if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1)
-                return;
-            StringBuilder license = new StringBuilder();
-            for (int i : l.getSelectedIndices()) {
-                if (i == 2) {
-                    license = new StringBuilder("public domain");
-                    break;
-                }
-                if (license.length() > 0) {
-                    license.append(", ");
-                }
-                license.append(URLS[i]);
-            }
-            copyright.setText(license.toString());
-            copyright.setCaretPosition(0);
-        });
-
-        authorActionListener.actionPerformed(null);
-        authorNameListener.keyReleased(null);
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/GpxImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GpxImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,192 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.GpxLayer;
-import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.tools.Logging;
-import org.xml.sax.SAXException;
-
-/**
- * File importer allowing to import GPX files (*.gpx/gpx.gz files).
- *
- */
-public class GpxImporter extends FileImporter {
-
-    /**
-     * Utility class containing imported GPX and marker layers, and a task to run after they are added to MapView.
-     */
-    public static class GpxImporterData {
-        /**
-         * The imported GPX layer. May be null if no GPX data.
-         */
-        private final GpxLayer gpxLayer;
-        /**
-         * The imported marker layer. May be null if no marker.
-         */
-        private final MarkerLayer markerLayer;
-        /**
-         * The task to run after GPX and/or marker layer has been added to MapView.
-         */
-        private final Runnable postLayerTask;
-
-        /**
-         * Constructs a new {@code GpxImporterData}.
-         * @param gpxLayer The imported GPX layer. May be null if no GPX data.
-         * @param markerLayer The imported marker layer. May be null if no marker.
-         * @param postLayerTask The task to run after GPX and/or marker layer has been added to MapView.
-         */
-        public GpxImporterData(GpxLayer gpxLayer, MarkerLayer markerLayer, Runnable postLayerTask) {
-            this.gpxLayer = gpxLayer;
-            this.markerLayer = markerLayer;
-            this.postLayerTask = postLayerTask;
-        }
-
-        /**
-         * Returns the imported GPX layer. May be null if no GPX data.
-         * @return the imported GPX layer. May be null if no GPX data.
-         */
-        public GpxLayer getGpxLayer() {
-            return gpxLayer;
-        }
-
-        /**
-         * Returns the imported marker layer. May be null if no marker.
-         * @return the imported marker layer. May be null if no marker.
-         */
-        public MarkerLayer getMarkerLayer() {
-            return markerLayer;
-        }
-
-        /**
-         * Returns the task to run after GPX and/or marker layer has been added to MapView.
-         * @return the task to run after GPX and/or marker layer has been added to MapView.
-         */
-        public Runnable getPostLayerTask() {
-            return postLayerTask;
-        }
-    }
-
-    /**
-     * Constructs a new {@code GpxImporter}.
-     */
-    public GpxImporter() {
-        super(getFileFilter());
-    }
-
-    /**
-     * Returns a GPX file filter (*.gpx and *.gpx.gz files).
-     * @return a GPX file filter
-     */
-    public static ExtensionFileFilter getFileFilter() {
-        return ExtensionFileFilter.newFilterWithArchiveExtensions(
-            "gpx", Main.pref.get("save.extension.gpx", "gpx"), tr("GPX Files"), true);
-    }
-
-    @Override
-    public void importData(File file, ProgressMonitor progressMonitor) throws IOException {
-        final String fileName = file.getName();
-
-        try (InputStream is = Compression.getUncompressedFileInputStream(file)) {
-            GpxReader r = new GpxReader(is);
-            boolean parsedProperly = r.parse(true);
-            r.getGpxData().storageFile = file;
-            addLayers(loadLayers(r.getGpxData(), parsedProperly, fileName, tr("Markers from {0}", fileName)));
-        } catch (SAXException e) {
-            Logging.error(e);
-            throw new IOException(tr("Parsing data for layer ''{0}'' failed", fileName), e);
-        }
-    }
-
-    /**
-     * Adds the specified GPX and marker layers to Map.main
-     * @param data The layers to add
-     * @see #loadLayers
-     */
-    public static void addLayers(final GpxImporterData data) {
-        // FIXME: remove UI stuff from the IO subsystem
-        GuiHelper.runInEDT(() -> {
-            if (data.markerLayer != null) {
-                MainApplication.getLayerManager().addLayer(data.markerLayer);
-            }
-            if (data.gpxLayer != null) {
-                MainApplication.getLayerManager().addLayer(data.gpxLayer);
-            }
-            data.postLayerTask.run();
-        });
-    }
-
-    /**
-     * Replies the new GPX and marker layers corresponding to the specified GPX data.
-     * @param data The GPX data
-     * @param parsedProperly True if GPX data has been properly parsed by {@link GpxReader#parse}
-     * @param gpxLayerName The GPX layer name
-     * @param markerLayerName The marker layer name
-     * @return the new GPX and marker layers corresponding to the specified GPX data, to be used with {@link #addLayers}
-     * @see #addLayers
-     */
-    public static GpxImporterData loadLayers(final GpxData data, final boolean parsedProperly,
-            final String gpxLayerName, String markerLayerName) {
-        GpxLayer gpxLayer = null;
-        MarkerLayer markerLayer = null;
-        if (data.hasRoutePoints() || data.hasTrackPoints()) {
-            gpxLayer = new GpxLayer(data, gpxLayerName, data.storageFile != null);
-        }
-        if (Main.pref.getBoolean("marker.makeautomarkers", true) && !data.waypoints.isEmpty()) {
-            markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer);
-            if (markerLayer.data.isEmpty()) {
-                markerLayer = null;
-            }
-        }
-        Runnable postLayerTask = () -> {
-            if (!parsedProperly) {
-                String msg;
-                if (data.storageFile == null) {
-                    msg = tr("Error occurred while parsing gpx data for layer ''{0}''. Only a part of the file will be available.",
-                            gpxLayerName);
-                } else {
-                    msg = tr("Error occurred while parsing gpx file ''{0}''. Only a part of the file will be available.",
-                            data.storageFile.getPath());
-                }
-                JOptionPane.showMessageDialog(null, msg);
-            }
-        };
-        return new GpxImporterData(gpxLayer, markerLayer, postLayerTask);
-    }
-
-    /**
-     * Replies the new GPX and marker layers corresponding to the specified GPX file.
-     * @param is input stream to GPX data
-     * @param associatedFile GPX file
-     * @param gpxLayerName The GPX layer name
-     * @param markerLayerName The marker layer name
-     * @param progressMonitor The progress monitor
-     * @return the new GPX and marker layers corresponding to the specified GPX file
-     * @throws IOException if an I/O error occurs
-     */
-    public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
-            final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException {
-        try {
-            final GpxReader r = new GpxReader(is);
-            final boolean parsedProperly = r.parse(true);
-            r.getGpxData().storageFile = associatedFile;
-            return loadLayers(r.getGpxData(), parsedProperly, gpxLayerName, markerLayerName);
-        } catch (SAXException e) {
-            Logging.error(e);
-            throw new IOException(tr("Parsing data for layer ''{0}'' failed", gpxLayerName), e);
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/JpgImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/JpgImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,131 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.layer.GpxLayer;
-import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-
-/**
- * File importer allowing to import geottaged images (*.jpg files).
- *
- */
-public class JpgImporter extends FileImporter {
-    private GpxLayer gpx;
-
-    /**
-     * The default file filter (only *.jpg files).
-     */
-    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
-            "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg)");
-
-    /**
-     * An alternate file filter that also includes folders.
-     * @since 5438
-     */
-    public static final ExtensionFileFilter FILE_FILTER_WITH_FOLDERS = new ExtensionFileFilter(
-            "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg, "+ tr("folder")+')');
-
-    /**
-     * Constructs a new {@code JpgImporter}.
-     */
-    public JpgImporter() {
-        this(false);
-    }
-
-    /**
-     * Constructs a new {@code JpgImporter} with folders selection, if wanted.
-     * @param includeFolders If true, includes folders in the file filter
-     * @since 5438
-     */
-    public JpgImporter(boolean includeFolders) {
-        super(includeFolders ? FILE_FILTER_WITH_FOLDERS : FILE_FILTER);
-    }
-
-    /**
-     * Constructs a new {@code JpgImporter} for the given GPX layer. Folders selection is allowed.
-     * @param gpx The GPX layer
-     */
-    public JpgImporter(GpxLayer gpx) {
-        this(true);
-        this.gpx = gpx;
-    }
-
-    @Override
-    public boolean acceptFile(File pathname) {
-        return super.acceptFile(pathname) || pathname.isDirectory();
-    }
-
-    @Override
-    public void importData(List<File> sel, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
-        progressMonitor.beginTask(tr("Looking for image files"), 1);
-        try {
-            List<File> files = new ArrayList<>();
-            Set<String> visitedDirs = new HashSet<>();
-            addRecursiveFiles(files, visitedDirs, sel, progressMonitor.createSubTaskMonitor(1, true));
-
-            if (progressMonitor.isCanceled())
-                return;
-
-            if (files.isEmpty())
-                throw new IOException(tr("No image files found."));
-
-            GeoImageLayer.create(files, gpx);
-        } finally {
-            progressMonitor.finishTask();
-        }
-    }
-
-    static void addRecursiveFiles(List<File> files, Set<String> visitedDirs, List<File> sel, ProgressMonitor progressMonitor)
-            throws IOException {
-
-        if (progressMonitor.isCanceled())
-            return;
-
-        progressMonitor.beginTask(null, sel.size());
-        try {
-            for (File f : sel) {
-                if (f.isDirectory()) {
-                    if (visitedDirs.add(f.getCanonicalPath())) { // Do not loop over symlinks
-                        File[] dirFiles = f.listFiles(); // Can be null for some strange directories (like lost+found)
-                        if (dirFiles != null) {
-                            addRecursiveFiles(files, visitedDirs, Arrays.asList(dirFiles), progressMonitor.createSubTaskMonitor(1, true));
-                        }
-                    } else {
-                        progressMonitor.worked(1);
-                    }
-                } else {
-                    if (FILE_FILTER.accept(f)) {
-                        files.add(f);
-                    }
-                    progressMonitor.worked(1);
-                }
-            }
-        } finally {
-            progressMonitor.finishTask();
-        }
-    }
-
-    @Override
-    public boolean isBatchImporter() {
-        return true;
-    }
-
-    /**
-     * Needs to be the last, to avoid problems.
-     */
-    @Override
-    public double getPriority() {
-        return -1000;
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/NMEAImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/NMEAImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,108 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.Notification;
-import org.openstreetmap.josm.gui.layer.GpxLayer;
-import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.io.GpxImporter.GpxImporterData;
-import org.openstreetmap.josm.io.nmea.NmeaReader;
-
-/**
- * File importer allowing to import NMEA-0183 files (*.nmea/nme/nma/log/txt files).
- * @since 1637
- */
-public class NMEAImporter extends FileImporter {
-
-    /**
-     * The NMEA file filter (*.nmea *.nme *.nma *.log *.txt files).
-     */
-    public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
-            "nmea,nme,nma,log,txt", "nmea", tr("NMEA-0183 Files"), false);
-
-    /**
-     * Constructs a new {@code NMEAImporter}.
-     */
-    public NMEAImporter() {
-        super(FILE_FILTER);
-    }
-
-    @Override
-    public void importData(File file, ProgressMonitor progressMonitor) throws IOException {
-        final String fn = file.getName();
-        try (InputStream fis = Compression.getUncompressedFileInputStream(file)) {
-            final NmeaReader r = new NmeaReader(fis);
-            if (r.getNumberOfCoordinates() > 0) {
-                r.data.storageFile = file;
-                final GpxLayer gpxLayer = new GpxLayer(r.data, fn, true);
-                final File fileFinal = file;
-
-                GuiHelper.runInEDT(() -> {
-                    MainApplication.getLayerManager().addLayer(gpxLayer);
-                    if (Main.pref.getBoolean("marker.makeautomarkers", true)) {
-                        MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), fileFinal, gpxLayer);
-                        if (!ml.data.isEmpty()) {
-                            MainApplication.getLayerManager().addLayer(ml);
-                        }
-                    }
-                });
-            }
-            showNmeaInfobox(r.getNumberOfCoordinates() > 0, r);
-        }
-    }
-
-    private static void showNmeaInfobox(boolean success, NmeaReader r) {
-        final StringBuilder msg = new StringBuilder(160).append("<html>")
-           .append(tr("Coordinates imported: {0}", r.getNumberOfCoordinates())).append("<br>")
-           .append(tr("Malformed sentences: {0}", r.getParserMalformed())).append("<br>")
-           .append(tr("Checksum errors: {0}", r.getParserChecksumErrors())).append("<br>");
-        if (!success) {
-            msg.append(tr("Unknown sentences: {0}", r.getParserUnknown())).append("<br>");
-        }
-        msg.append(tr("Zero coordinates: {0}", r.getParserZeroCoordinates()))
-           .append("</html>");
-        if (success) {
-            SwingUtilities.invokeLater(() -> new Notification(
-                    "<h3>" + tr("NMEA import success:") + "</h3>" + msg.toString())
-                    .setIcon(JOptionPane.INFORMATION_MESSAGE)
-                    .show());
-        } else {
-            HelpAwareOptionPane.showMessageDialogInEDT(
-                    Main.parent,
-                    msg.toString(),
-                    tr("NMEA import failure!"),
-                    JOptionPane.ERROR_MESSAGE, null);
-        }
-    }
-
-    /**
-     * Replies the new GPX and marker layers corresponding to the specified NMEA file.
-     * @param is input stream to NMEA 0183 data
-     * @param associatedFile NMEA file
-     * @param gpxLayerName The GPX layer name
-     * @param markerLayerName The marker layer name
-     * @return the new GPX and marker layers corresponding to the specified NMEA file
-     * @throws IOException if an I/O error occurs
-     */
-    public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
-            final String gpxLayerName, String markerLayerName) throws IOException {
-        final NmeaReader r = new NmeaReader(is);
-        final boolean parsedProperly = r.getNumberOfCoordinates() > 0;
-        r.data.storageFile = associatedFile;
-        return GpxImporter.loadLayers(r.data, parsedProperly, gpxLayerName, markerLayerName);
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/NoteExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/NoteExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,47 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.NoteLayer;
-import org.openstreetmap.josm.tools.Logging;
-
-/**
- * Exporter to write note data to an XML file
- */
-public class NoteExporter extends FileExporter {
-
-    /** File extension filter for .osn files */
-    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
-            "osn", "osn", tr("Note Files") + " (*.osn)");
-
-    /** Create a new note exporter with the default .osn file filter */
-    public NoteExporter() {
-        super(FILE_FILTER);
-    }
-
-    @Override
-    public boolean acceptFile(File pathname, Layer layer) {
-        if (!(layer instanceof NoteLayer))
-            return false;
-        return super.acceptFile(pathname, layer);
-    }
-
-    @Override
-    public void exportData(File file, Layer layer) throws IOException {
-        Logging.info("exporting notes to file: " + file);
-        if (layer instanceof NoteLayer) {
-            try (OutputStream os = new FileOutputStream(file);
-                 NoteWriter writer = new NoteWriter(os)) {
-                writer.write(((NoteLayer) layer).getNoteData());
-            }
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/NoteImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/NoteImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,77 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.data.notes.Note;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.NoteLayer;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.tools.Logging;
-import org.xml.sax.SAXException;
-
-/**
- * File importer that reads note dump files (*.osn, .osn.gz and .osn.bz2)
- * @since 7538
- */
-public class NoteImporter extends FileImporter {
-
-    private static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
-            "osn", "osn", tr("Note Files"), true);
-
-    /** Create an importer for note dump files */
-    public NoteImporter() {
-        super(FILE_FILTER);
-    }
-
-    @Override
-    public void importData(final File file, ProgressMonitor progressMonitor) throws IOException {
-        if (Logging.isDebugEnabled()) {
-            Logging.debug("importing notes file {0}", file.getAbsolutePath());
-        }
-        try (InputStream is = Compression.getUncompressedFileInputStream(file)) {
-            final NoteLayer layer = loadLayer(is, file, file.getName(), progressMonitor);
-            if (!MainApplication.getLayerManager().containsLayer(layer)) {
-                MainApplication.getLayerManager().addLayer(layer);
-            }
-        } catch (SAXException e) {
-            Logging.error("error opening up notes file");
-            Logging.error(e);
-            throw new IOException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * Load note layer from InputStream.
-     * @param in input stream
-     * @param associatedFile filename of data (can be <code>null</code> if the stream does not come from a file)
-     * @param layerName name of generated layer
-     * @param progressMonitor handler for progress monitoring and canceling
-     * @return note layer
-     * @throws IOException if any I/O error occurs
-     * @throws SAXException if any SAX error occurs
-     * @since 9746
-     */
-    public NoteLayer loadLayer(InputStream in, final File associatedFile, final String layerName, ProgressMonitor progressMonitor)
-            throws SAXException, IOException {
-        final List<Note> fileNotes = new NoteReader(in).parse();
-        List<NoteLayer> noteLayers = null;
-        if (MainApplication.getMap() != null) {
-            noteLayers = MainApplication.getLayerManager().getLayersOfType(NoteLayer.class);
-        }
-        final NoteLayer layer;
-        if (noteLayers != null && !noteLayers.isEmpty()) {
-            layer = noteLayers.get(0);
-            layer.getNoteData().addNotes(fileNotes);
-        } else {
-            layer = new NoteLayer(fileNotes, associatedFile != null ? associatedFile.getName() : tr("Notes"));
-        }
-        return layer;
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/OsmBzip2Exporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmBzip2Exporter.java	(revision 12670)
+++ 	(revision )
@@ -1,23 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-
-/**
- * OSM data exporter that compresses it in Bzip2 format.
- */
-public class OsmBzip2Exporter extends OsmExporter {
-
-    /**
-     * Constructs a new {@code OsmBzip2Exporter}.
-     */
-    public OsmBzip2Exporter() {
-        super(new ExtensionFileFilter(
-            "osm.bz2,osm.bz", "osm.bz2", tr("OSM Server Files bzip2 compressed") + " (*.osm.bz2, *.osm.bz)"));
-    }
-
-    // compression handling is performed in super-class
-
-}
Index: trunk/src/org/openstreetmap/josm/io/OsmChangeImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmChangeImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,73 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.tools.Logging;
-
-/**
- * File importer that reads OSM change files (*.osc).
- * @see <a href="http://wiki.openstreetmap.org/wiki/OsmChange">OsmChange</a>
- */
-public class OsmChangeImporter extends FileImporter {
-
-    public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
-            "osc", "osc", tr("OsmChange File"), true);
-
-    /**
-     * Constructs a new {@code OsmChangeImporter}.
-     */
-    public OsmChangeImporter() {
-        super(FILE_FILTER);
-    }
-
-    public OsmChangeImporter(ExtensionFileFilter filter) {
-        super(filter);
-    }
-
-    @Override
-    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
-        try {
-            importData(Compression.getUncompressedFileInputStream(file), file, progressMonitor);
-        } catch (FileNotFoundException e) {
-            Logging.error(e);
-            throw new IOException(tr("File ''{0}'' does not exist.", file.getName()), e);
-        }
-    }
-
-    protected void importData(InputStream in, final File associatedFile, ProgressMonitor progressMonitor) throws IllegalDataException {
-        final DataSet dataSet = OsmChangeReader.parseDataSet(in, progressMonitor);
-        final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
-        addDataLayer(dataSet, layer, associatedFile.getPath());
-    }
-
-    protected void addDataLayer(final DataSet dataSet, final OsmDataLayer layer, final String filePath) {
-        // FIXME: remove UI stuff from IO subsystem
-        //
-        GuiHelper.runInEDT(() -> {
-            if (dataSet.allPrimitives().isEmpty()) {
-                JOptionPane.showMessageDialog(
-                        Main.parent,
-                        tr("No data found in file {0}.", filePath),
-                        tr("Open OsmChange file"),
-                        JOptionPane.INFORMATION_MESSAGE);
-            }
-            MainApplication.getLayerManager().addLayer(layer);
-            layer.onPostLoadFromFile();
-        });
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/OsmExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,136 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-import java.text.MessageFormat;
-
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.Utils;
-
-/**
- * Exports data to an .osm file.
- * @since 1949
- */
-public class OsmExporter extends FileExporter {
-
-    /**
-     * Constructs a new {@code OsmExporter}.
-     */
-    public OsmExporter() {
-        super(new ExtensionFileFilter(
-            "osm,xml", "osm", tr("OSM Server Files") + " (*.osm)"));
-    }
-
-    /**
-     * Constructs a new {@code OsmExporter}.
-     * @param filter The extension file filter
-     */
-    public OsmExporter(ExtensionFileFilter filter) {
-        super(filter);
-    }
-
-    @Override
-    public boolean acceptFile(File pathname, Layer layer) {
-        if (!(layer instanceof OsmDataLayer))
-            return false;
-        return super.acceptFile(pathname, layer);
-    }
-
-    @Override
-    public void exportData(File file, Layer layer) throws IOException {
-        exportData(file, layer, false);
-    }
-
-    /**
-     * Exports OSM data to the given file.
-     * @param file Output file
-     * @param layer Data layer. Must be an instance of {@link OsmDataLayer}.
-     * @param noBackup if {@code true}, the potential backup file created if the output file already exists will be deleted
-     *                 after a successful export
-     * @throws IllegalArgumentException if {@code layer} is not an instance of {@code OsmDataLayer}
-     */
-    public void exportData(File file, Layer layer, boolean noBackup) {
-        if (!(layer instanceof OsmDataLayer)) {
-            throw new IllegalArgumentException(
-                    MessageFormat.format("Expected instance of OsmDataLayer. Got ''{0}''.", layer.getClass().getName()));
-        }
-        save(file, (OsmDataLayer) layer, noBackup);
-    }
-
-    protected static OutputStream getOutputStream(File file) throws IOException {
-        return Compression.getCompressedFileOutputStream(file);
-    }
-
-    private void save(File file, OsmDataLayer layer, boolean noBackup) {
-        File tmpFile = null;
-        try {
-            // use a tmp file because if something errors out in the process of writing the file,
-            // we might just end up with a truncated file.  That can destroy lots of work.
-            if (file.exists()) {
-                tmpFile = new File(file.getPath() + '~');
-                Utils.copyFile(file, tmpFile);
-            }
-
-            doSave(file, layer);
-            if ((noBackup || !Main.pref.getBoolean("save.keepbackup", false)) && tmpFile != null) {
-                Utils.deleteFile(tmpFile);
-            }
-            layer.onPostSaveToFile();
-        } catch (IOException e) {
-            Logging.error(e);
-            JOptionPane.showMessageDialog(
-                    Main.parent,
-                    tr("<html>An error occurred while saving.<br>Error is:<br>{0}</html>",
-                            Utils.escapeReservedCharactersHTML(e.getMessage())),
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE
-            );
-
-            try {
-                // if the file save failed, then the tempfile will not be deleted. So, restore the backup if we made one.
-                if (tmpFile != null && tmpFile.exists()) {
-                    Utils.copyFile(tmpFile, file);
-                }
-            } catch (IOException e2) {
-                Logging.error(e2);
-                JOptionPane.showMessageDialog(
-                        Main.parent,
-                        tr("<html>An error occurred while restoring backup file.<br>Error is:<br>{0}</html>",
-                                Utils.escapeReservedCharactersHTML(e2.getMessage())),
-                        tr("Error"),
-                        JOptionPane.ERROR_MESSAGE
-                );
-            }
-        }
-    }
-
-    protected void doSave(File file, OsmDataLayer layer) throws IOException {
-        // create outputstream and wrap it with gzip or bzip, if necessary
-        try (
-            OutputStream out = getOutputStream(file);
-            Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
-            OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, layer.data.getVersion())
-        ) {
-            layer.data.getReadLock().lock();
-            try {
-                w.writeLayer(layer);
-            } finally {
-                layer.data.getReadLock().unlock();
-            }
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/OsmGzipExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmGzipExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,23 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-
-/**
- * OSM data exporter that compresses it in GZip format.
- */
-public class OsmGzipExporter extends OsmExporter {
-
-    /**
-     * Constructs a new {@code OsmGzipExporter}.
-     */
-    public OsmGzipExporter() {
-        super(new ExtensionFileFilter(
-            "osm.gz", "osm.gz", tr("OSM Server Files gzip compressed") + " (*.osm.gz)"));
-    }
-
-    // compression handling is performed in super-class
-
-}
Index: trunk/src/org/openstreetmap/josm/io/OsmImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,154 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.tools.Logging;
-
-/**
- * File importer that reads *.osm data files. (main storage format for OSM data
- * in JOSM)
- */
-public class OsmImporter extends FileImporter {
-
-    /**
-     * The OSM file filter (*.osm and *.xml files).
-     */
-    public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
-            "osm,xml", "osm", tr("OSM Server Files") + " (*.osm, *.osm.gz, *.osm.bz2, *.osm.zip, *.xml)",
-            ExtensionFileFilter.AddArchiveExtension.NONE, Arrays.asList("gz", "bz", "bz2", "zip"));
-
-    /**
-     * Utility class containing imported OSM layer, and a task to run after it is added to MapView.
-     */
-    public static class OsmImporterData {
-
-        private final OsmDataLayer layer;
-        private final Runnable postLayerTask;
-
-        public OsmImporterData(OsmDataLayer layer, Runnable postLayerTask) {
-            this.layer = layer;
-            this.postLayerTask = postLayerTask;
-        }
-
-        public OsmDataLayer getLayer() {
-            return layer;
-        }
-
-        public Runnable getPostLayerTask() {
-            return postLayerTask;
-        }
-    }
-
-    /**
-     * Constructs a new {@code OsmImporter}.
-     */
-    public OsmImporter() {
-        super(FILE_FILTER);
-    }
-
-    /**
-     * Constructs a new {@code OsmImporter} with the given extension file filter.
-     * @param filter The extension file filter
-     */
-    public OsmImporter(ExtensionFileFilter filter) {
-        super(filter);
-    }
-
-    /**
-     * Imports OSM data from file
-     * @param file file to read data from
-     * @param progressMonitor handler for progress monitoring and canceling
-     */
-    @Override
-    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
-        try (InputStream in = Compression.getUncompressedFileInputStream(file)) {
-            importData(in, file, progressMonitor);
-        } catch (FileNotFoundException e) {
-            Logging.error(e);
-            throw new IOException(tr("File ''{0}'' does not exist.", file.getName()), e);
-        }
-    }
-
-    /**
-     * Imports OSM data from stream
-     * @param in input stream
-     * @param associatedFile filename of data (layer name will be generated from name of file)
-     * @param pm handler for progress monitoring and canceling
-     * @throws IllegalDataException if an error was found while parsing the OSM data
-     */
-    protected void importData(InputStream in, final File associatedFile, ProgressMonitor pm) throws IllegalDataException {
-        final OsmImporterData data = loadLayer(in, associatedFile,
-                associatedFile == null ? OsmDataLayer.createNewName() : associatedFile.getName(), pm);
-
-        // FIXME: remove UI stuff from IO subsystem
-        GuiHelper.runInEDT(() -> {
-            OsmDataLayer layer = data.getLayer();
-            MainApplication.getLayerManager().addLayer(layer);
-            data.getPostLayerTask().run();
-            data.getLayer().onPostLoadFromFile();
-        });
-    }
-
-    /**
-     * Load osm data layer from InputStream.
-     * @param in input stream
-     * @param associatedFile filename of data (can be <code>null</code> if the stream does not come from a file)
-     * @param layerName name of generated layer
-     * @param progressMonitor handler for progress monitoring and canceling
-     * @return Utility class containing imported OSM layer, and a task to run after it is added to MapView
-     * @throws IllegalDataException if an error was found while parsing the OSM data
-     */
-    public OsmImporterData loadLayer(InputStream in, final File associatedFile, final String layerName, ProgressMonitor progressMonitor)
-            throws IllegalDataException {
-        final DataSet dataSet = parseDataSet(in, progressMonitor);
-        if (dataSet == null) {
-            throw new IllegalDataException(tr("Invalid dataset"));
-        }
-        OsmDataLayer layer = createLayer(dataSet, associatedFile, layerName);
-        Runnable postLayerTask = createPostLayerTask(dataSet, associatedFile, layerName, layer);
-        return new OsmImporterData(layer, postLayerTask);
-    }
-
-    protected DataSet parseDataSet(InputStream in, ProgressMonitor progressMonitor) throws IllegalDataException {
-        return OsmReader.parseDataSet(in, progressMonitor);
-    }
-
-    protected OsmDataLayer createLayer(final DataSet dataSet, final File associatedFile, final String layerName) {
-        return new OsmDataLayer(dataSet, layerName, associatedFile);
-    }
-
-    protected Runnable createPostLayerTask(final DataSet dataSet, final File associatedFile, final String layerName, final OsmDataLayer layer) {
-        return () -> {
-            if (dataSet.allPrimitives().isEmpty()) {
-                String msg;
-                if (associatedFile == null) {
-                    msg = tr("No data found for layer ''{0}''.", layerName);
-                } else {
-                    msg = tr("No data found in file ''{0}''.", associatedFile.getPath());
-                }
-                JOptionPane.showMessageDialog(
-                        Main.parent,
-                        msg,
-                        tr("Open OSM file"),
-                        JOptionPane.INFORMATION_MESSAGE);
-            }
-            layer.onPostLoadFromFile();
-        };
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/ValidatorErrorExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/ValidatorErrorExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,51 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.layer.ValidatorLayer;
-import org.openstreetmap.josm.tools.Logging;
-
-/**
- * Exporter to write validator errors to an XML file.
- * @since 12667
- */
-public class ValidatorErrorExporter extends FileExporter {
-
-    /** File extension filter for .xml files */
-    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
-            "xml", "xml", tr("Validation Error Files") + " (*.xml)");
-
-    /** Create a new validator error exporter with the default .xml file filter */
-    public ValidatorErrorExporter() {
-        super(FILE_FILTER);
-    }
-
-    @Override
-    public boolean acceptFile(File pathname, Layer layer) {
-        if (!(layer instanceof ValidatorLayer))
-            return false;
-        return super.acceptFile(pathname, layer);
-    }
-
-    @Override
-    public void exportData(File file, Layer layer) throws IOException {
-        OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
-        if (layer instanceof ValidatorLayer && editLayer != null) {
-            Logging.info("exporting validation errors to file: " + file);
-            try (OutputStream os = new FileOutputStream(file);
-                 ValidatorErrorWriter writer = new ValidatorErrorWriter(os)) {
-                writer.write(editLayer.validationErrors);
-            }
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/WMSLayerExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/WMSLayerExporter.java	(revision 12670)
+++ 	(revision )
@@ -1,53 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-
-import org.openstreetmap.josm.data.Preferences;
-import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
-
-/**
- * Export a WMS layer to a serialized binary file that can be imported later via {@link WMSLayerImporter}.
- *
- * @since 5457
- */
-public class WMSLayerExporter extends FileExporter {
-
-    /** Which version of the file we export */
-    public static final int CURRENT_FILE_VERSION = 6;
-
-    /**
-     * Constructs a new {@code WMSLayerExporter}
-     */
-    public WMSLayerExporter() {
-        super(WMSLayerImporter.FILE_FILTER);
-    }
-
-    @Override
-    public void exportData(File file, Layer layer) throws IOException {
-        CheckParameterUtil.ensureParameterNotNull(file, "file");
-        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
-
-        if (layer instanceof AbstractTileSourceLayer) {
-            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
-                oos.writeInt(CURRENT_FILE_VERSION); // file version
-                oos.writeObject(MainApplication.getMap().mapView.getCenter());
-                ImageryPreferenceEntry entry = new ImageryPreferenceEntry(((AbstractTileSourceLayer) layer).getInfo());
-                oos.writeObject(Preferences.serializeStruct(entry, ImageryPreferenceEntry.class));
-            }
-        }
-    }
-
-    @Override
-    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
-        setEnabled(e.getSource().getActiveLayer() instanceof AbstractTileSourceLayer);
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/WMSLayerImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/WMSLayerImporter.java	(revision 12670)
+++ 	(revision )
@@ -1,94 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InvalidClassException;
-import java.io.ObjectInputStream;
-import java.util.Map;
-
-import org.openstreetmap.josm.actions.ExtensionFileFilter;
-import org.openstreetmap.josm.data.Preferences;
-import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.imagery.ImageryInfo;
-import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.layer.ImageryLayer;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.tools.CheckParameterUtil;
-
-/**
- * Import a WMS layer from a serialized binary file previously exported via {@link WMSLayerExporter}.
- * @since 5457
- */
-public class WMSLayerImporter extends FileImporter {
-
-    /**
-     * The file filter used in "open" and "save" dialogs for WMS layers.
-     */
-    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
-            "wms", "wms", tr("WMS Files (*.wms)"));
-
-    /**
-     * Constructs a new {@code WMSLayerImporter}.
-     */
-    public WMSLayerImporter() {
-        super(FILE_FILTER);
-    }
-
-    @Override
-    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
-        CheckParameterUtil.ensureParameterNotNull(file, "file");
-        final EastNorth zoomTo;
-        ImageryInfo info = null;
-        final ImageryLayer layer;
-
-        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
-            int sfv = ois.readInt();
-            if (sfv < 5) {
-                throw new InvalidClassException(tr("Unsupported WMS file version; found {0}, expected {1}", sfv, 5));
-            } else if (sfv == 5) {
-                ois.readInt(); // dax - not needed
-                ois.readInt(); // day - not needed
-                zoomTo = null;
-
-                int imageSize = ois.readInt();
-                double pixelPerDegree = ois.readDouble();
-
-                String name = (String) ois.readObject();
-                String extendedUrl = (String) ois.readObject();
-
-                info = new ImageryInfo(name);
-                info.setExtendedUrl(extendedUrl);
-                info.setPixelPerDegree(pixelPerDegree);
-                info.setTileSize(imageSize);
-            } else if (sfv == WMSLayerExporter.CURRENT_FILE_VERSION) {
-                zoomTo = (EastNorth) ois.readObject();
-
-                @SuppressWarnings("unchecked")
-                ImageryPreferenceEntry entry = Preferences.deserializeStruct(
-                        (Map<String, String>) ois.readObject(),
-                        ImageryPreferenceEntry.class);
-                info = new ImageryInfo(entry);
-            } else {
-                throw new InvalidClassException(tr("Unsupported WMS file version; found {0}, expected {1}", sfv, 6));
-            }
-        } catch (ClassNotFoundException e) {
-            throw new IllegalDataException(e);
-        }
-        layer = ImageryLayer.create(info);
-
-
-        // FIXME: remove UI stuff from IO subsystem
-        GuiHelper.runInEDT(() -> {
-            MainApplication.getLayerManager().addLayer(layer);
-            if (zoomTo != null) {
-                MainApplication.getMap().mapView.zoomTo(zoomTo);
-            }
-        });
-    }
-}
Index: trunk/src/org/openstreetmap/josm/io/session/GpxTracksSessionImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/GpxTracksSessionImporter.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/io/session/GpxTracksSessionImporter.java	(revision 12671)
@@ -13,10 +13,10 @@
 import javax.xml.xpath.XPathFactory;
 
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
+import org.openstreetmap.josm.gui.io.importexport.NMEAImporter;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.io.GpxImporter;
 import org.openstreetmap.josm.io.IllegalDataException;
-import org.openstreetmap.josm.io.NMEAImporter;
 import org.w3c.dom.Element;
 
Index: trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/io/session/MarkerSessionImporter.java	(revision 12671)
@@ -14,9 +14,9 @@
 import javax.xml.xpath.XPathFactory;
 
+import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.io.GpxImporter;
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.session.SessionReader.ImportSupport;
Index: trunk/src/org/openstreetmap/josm/io/session/NoteSessionImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/NoteSessionImporter.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/io/session/NoteSessionImporter.java	(revision 12671)
@@ -13,9 +13,9 @@
 import javax.xml.xpath.XPathFactory;
 
+import org.openstreetmap.josm.gui.io.importexport.NoteImporter;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.NoteLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.IllegalDataException;
-import org.openstreetmap.josm.io.NoteImporter;
 import org.openstreetmap.josm.io.session.SessionReader.ImportSupport;
 import org.w3c.dom.Element;
Index: trunk/src/org/openstreetmap/josm/io/session/OsmDataSessionImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/OsmDataSessionImporter.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/io/session/OsmDataSessionImporter.java	(revision 12671)
@@ -13,9 +13,9 @@
 import javax.xml.xpath.XPathFactory;
 
+import org.openstreetmap.josm.gui.io.importexport.OsmImporter;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.IllegalDataException;
-import org.openstreetmap.josm.io.OsmImporter;
 import org.openstreetmap.josm.io.session.SessionReader.ImportSupport;
 import org.w3c.dom.Element;
Index: trunk/src/org/openstreetmap/josm/io/session/SessionImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/SessionImporter.java	(revision 12670)
+++ trunk/src/org/openstreetmap/josm/io/session/SessionImporter.java	(revision 12671)
@@ -10,6 +10,6 @@
 import org.openstreetmap.josm.actions.SessionLoadAction.Loader;
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.io.importexport.FileImporter;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.io.FileImporter;
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.tools.Utils;
Index: trunk/test/unit/org/openstreetmap/josm/gui/io/importexport/JpgImporterTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/io/importexport/JpgImporterTest.java	(revision 12671)
+++ trunk/test/unit/org/openstreetmap/josm/gui/io/importexport/JpgImporterTest.java	(revision 12671)
@@ -0,0 +1,46 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Unit tests of {@link JpgImporter} class.
+ */
+public class JpgImporterTest {
+
+    /**
+     * Setup test
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/14868">Bug #14868</a>.
+     * @throws IOException if an error occurs
+     */
+    @Test
+    public void testTicket14868() throws IOException {
+        List<File> files = new ArrayList<>();
+        JpgImporter.addRecursiveFiles(files, new HashSet<>(), Arrays.asList(
+                new File("foo.jpg"), new File("foo.jpeg")
+                ), NullProgressMonitor.INSTANCE);
+        assertEquals(2, files.size());
+        assertEquals("foo.jpg", files.get(0).getName());
+        assertEquals("foo.jpeg", files.get(1).getName());
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/io/importexport/NoteImporterTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/io/importexport/NoteImporterTest.java	(revision 12671)
+++ trunk/test/unit/org/openstreetmap/josm/gui/io/importexport/NoteImporterTest.java	(revision 12671)
@@ -0,0 +1,36 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Unit tests of {@link NoteImporter} class.
+ */
+public class NoteImporterTest {
+
+    /**
+     * Use the test rules to remove any layers and reset state.
+     */
+    @Rule
+    public final JOSMTestRules rules = new JOSMTestRules();
+
+    /**
+     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12531">Bug #12531</a>.
+     */
+    @Test
+    public void testTicket12531() {
+        MainApplication.getLayerManager().resetState();
+        assertNull(MainApplication.getMap());
+        assertTrue(new NoteImporter().importDataHandleExceptions(
+                new File(TestUtils.getRegressionDataFile(12531, "notes.osn")), null));
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/io/JpgImporterTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/JpgImporterTest.java	(revision 12670)
+++ 	(revision )
@@ -1,46 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * Unit tests of {@link JpgImporter} class.
- */
-public class JpgImporterTest {
-
-    /**
-     * Setup test
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
-     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/14868">Bug #14868</a>.
-     * @throws IOException if an error occurs
-     */
-    @Test
-    public void testTicket14868() throws IOException {
-        List<File> files = new ArrayList<>();
-        JpgImporter.addRecursiveFiles(files, new HashSet<>(), Arrays.asList(
-                new File("foo.jpg"), new File("foo.jpeg")
-                ), NullProgressMonitor.INSTANCE);
-        assertEquals(2, files.size());
-        assertEquals("foo.jpg", files.get(0).getName());
-        assertEquals("foo.jpeg", files.get(1).getName());
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/io/NoteImporterTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/NoteImporterTest.java	(revision 12670)
+++ 	(revision )
@@ -1,36 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.io;
-
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-/**
- * Unit tests of {@link NoteImporter} class.
- */
-public class NoteImporterTest {
-
-    /**
-     * Use the test rules to remove any layers and reset state.
-     */
-    @Rule
-    public final JOSMTestRules rules = new JOSMTestRules();
-
-    /**
-     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12531">Bug #12531</a>.
-     */
-    @Test
-    public void testTicket12531() {
-        MainApplication.getLayerManager().resetState();
-        assertNull(MainApplication.getMap());
-        assertTrue(new NoteImporter().importDataHandleExceptions(
-                new File(TestUtils.getRegressionDataFile(12531, "notes.osn")), null));
-    }
-}
