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