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