001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.io.importexport; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.File; 007import java.io.IOException; 008import java.io.OutputStream; 009import java.io.OutputStreamWriter; 010import java.io.PrintWriter; 011import java.io.Writer; 012import java.nio.charset.StandardCharsets; 013import java.nio.file.InvalidPathException; 014import java.text.MessageFormat; 015 016import javax.swing.JOptionPane; 017 018import org.openstreetmap.josm.actions.ExtensionFileFilter; 019import org.openstreetmap.josm.gui.MainApplication; 020import org.openstreetmap.josm.gui.layer.Layer; 021import org.openstreetmap.josm.gui.layer.OsmDataLayer; 022import org.openstreetmap.josm.io.Compression; 023import org.openstreetmap.josm.io.OsmWriter; 024import org.openstreetmap.josm.io.OsmWriterFactory; 025import org.openstreetmap.josm.spi.preferences.Config; 026import org.openstreetmap.josm.tools.Logging; 027import org.openstreetmap.josm.tools.Utils; 028 029/** 030 * Exports data to an .osm file. 031 * @since 1949 032 */ 033public class OsmExporter extends FileExporter { 034 035 /** 036 * Constructs a new {@code OsmExporter}. 037 */ 038 public OsmExporter() { 039 super(new ExtensionFileFilter( 040 "osm,xml", "osm", tr("OSM Server Files") + " (*.osm)")); 041 } 042 043 /** 044 * Constructs a new {@code OsmExporter}. 045 * @param filter The extension file filter 046 */ 047 public OsmExporter(ExtensionFileFilter filter) { 048 super(filter); 049 } 050 051 @Override 052 public boolean acceptFile(File pathname, Layer layer) { 053 if (!(layer instanceof OsmDataLayer)) 054 return false; 055 return super.acceptFile(pathname, layer); 056 } 057 058 @Override 059 public void exportData(File file, Layer layer) throws IOException { 060 exportData(file, layer, false); 061 } 062 063 /** 064 * Exports OSM data to the given file. 065 * @param file Output file 066 * @param layer Data layer. Must be an instance of {@link OsmDataLayer}. 067 * @param noBackup if {@code true}, the potential backup file created if the output file already exists will be deleted 068 * after a successful export 069 * @throws IllegalArgumentException if {@code layer} is not an instance of {@code OsmDataLayer} 070 */ 071 public void exportData(File file, Layer layer, boolean noBackup) { 072 if (!(layer instanceof OsmDataLayer)) { 073 throw new IllegalArgumentException( 074 MessageFormat.format("Expected instance of OsmDataLayer. Got ''{0}''.", layer.getClass().getName())); 075 } 076 save(file, (OsmDataLayer) layer, noBackup); 077 } 078 079 protected static OutputStream getOutputStream(File file) throws IOException { 080 return Compression.getCompressedFileOutputStream(file); 081 } 082 083 private void save(File file, OsmDataLayer layer, boolean noBackup) { 084 File tmpFile = null; 085 try { 086 // use a tmp file because if something errors out in the process of writing the file, 087 // we might just end up with a truncated file. That can destroy lots of work. 088 if (file.exists()) { 089 tmpFile = new File(file.getPath() + '~'); 090 Utils.copyFile(file, tmpFile); 091 } 092 093 doSave(file, layer); 094 if ((noBackup || !Config.getPref().getBoolean("save.keepbackup", false)) && tmpFile != null) { 095 Utils.deleteFile(tmpFile); 096 } 097 layer.onPostSaveToFile(); 098 } catch (IOException | InvalidPathException e) { 099 Logging.error(e); 100 JOptionPane.showMessageDialog( 101 MainApplication.getMainFrame(), 102 tr("<html>An error occurred while saving.<br>Error is:<br>{0}</html>", 103 Utils.escapeReservedCharactersHTML(e.getClass().getSimpleName() + " - " + e.getMessage())), 104 tr("Error"), 105 JOptionPane.ERROR_MESSAGE 106 ); 107 108 try { 109 // if the file save failed, then the tempfile will not be deleted. So, restore the backup if we made one. 110 if (tmpFile != null && tmpFile.exists()) { 111 Utils.copyFile(tmpFile, file); 112 } 113 } catch (IOException | InvalidPathException e2) { 114 Logging.error(e2); 115 JOptionPane.showMessageDialog( 116 MainApplication.getMainFrame(), 117 tr("<html>An error occurred while restoring backup file.<br>Error is:<br>{0}</html>", 118 Utils.escapeReservedCharactersHTML(e2.getMessage())), 119 tr("Error"), 120 JOptionPane.ERROR_MESSAGE 121 ); 122 } 123 } 124 } 125 126 protected void doSave(File file, OsmDataLayer layer) throws IOException { 127 // create outputstream and wrap it with gzip, xz or bzip, if necessary 128 try ( 129 OutputStream out = getOutputStream(file); 130 Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); 131 OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, layer.data.getVersion()) 132 ) { 133 layer.data.getReadLock().lock(); 134 try { 135 w.write(layer.data); 136 } finally { 137 layer.data.getReadLock().unlock(); 138 } 139 } 140 } 141}