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