source: josm/trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmExporter.java@ 14982

Last change on this file since 14982 was 14982, checked in by GerdP, 5 years ago

fix #17237: Poor error handling with write protected files

  • Property svn:eol-style set to native
File size: 5.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io.importexport;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.File;
7import java.io.IOException;
8import java.io.OutputStream;
9import java.io.OutputStreamWriter;
10import java.io.PrintWriter;
11import java.io.Writer;
12import java.nio.charset.StandardCharsets;
13import java.nio.file.AccessDeniedException;
14import java.nio.file.InvalidPathException;
15import java.text.MessageFormat;
16
17import javax.swing.JOptionPane;
18
19import org.openstreetmap.josm.actions.ExtensionFileFilter;
20import org.openstreetmap.josm.gui.MainApplication;
21import org.openstreetmap.josm.gui.layer.Layer;
22import org.openstreetmap.josm.gui.layer.OsmDataLayer;
23import org.openstreetmap.josm.io.Compression;
24import org.openstreetmap.josm.io.OsmWriter;
25import org.openstreetmap.josm.io.OsmWriterFactory;
26import org.openstreetmap.josm.spi.preferences.Config;
27import org.openstreetmap.josm.tools.Logging;
28import org.openstreetmap.josm.tools.Utils;
29
30/**
31 * Exports data to an .osm file.
32 * @since 1949
33 */
34public class OsmExporter extends FileExporter {
35
36 /**
37 * Constructs a new {@code OsmExporter}.
38 */
39 public OsmExporter() {
40 super(new ExtensionFileFilter(
41 "osm,xml", "osm", tr("OSM Server Files") + " (*.osm)"));
42 }
43
44 /**
45 * Constructs a new {@code OsmExporter}.
46 * @param filter The extension file filter
47 */
48 public OsmExporter(ExtensionFileFilter filter) {
49 super(filter);
50 }
51
52 @Override
53 public boolean acceptFile(File pathname, Layer layer) {
54 if (!(layer instanceof OsmDataLayer))
55 return false;
56 return super.acceptFile(pathname, layer);
57 }
58
59 @Override
60 public void exportData(File file, Layer layer) throws IOException {
61 exportData(file, layer, false);
62 }
63
64 /**
65 * Exports OSM data to the given file.
66 * @param file Output file
67 * @param layer Data layer. Must be an instance of {@link OsmDataLayer}.
68 * @param noBackup if {@code true}, the potential backup file created if the output file already exists will be deleted
69 * after a successful export
70 * @throws IOException in case of IO errors
71 * @throws InvalidPathException when file name cannot be converted into a Path
72 * @throws IllegalArgumentException if {@code layer} is not an instance of {@code OsmDataLayer}
73 */
74 public void exportData(File file, Layer layer, boolean noBackup) throws IOException {
75 if (!(layer instanceof OsmDataLayer)) {
76 throw new IllegalArgumentException(
77 MessageFormat.format("Expected instance of OsmDataLayer. Got ''{0}''.", layer.getClass().getName()));
78 }
79 save(file, (OsmDataLayer) layer, noBackup);
80 }
81
82 protected static OutputStream getOutputStream(File file) throws IOException {
83 return Compression.getCompressedFileOutputStream(file);
84 }
85
86 private void save(File file, OsmDataLayer layer, boolean noBackup) throws IOException {
87 File tmpFile = null;
88 try {
89 if (file.exists() && !file.canWrite()) {
90 throw new AccessDeniedException(file.toString());
91 }
92
93 // use a tmp file because if something errors out in the process of writing the file,
94 // we might just end up with a truncated file. That can destroy lots of work.
95 if (file.exists()) {
96 tmpFile = new File(file.getPath() + '~');
97 Utils.copyFile(file, tmpFile);
98 }
99
100 doSave(file, layer);
101 if ((noBackup || !Config.getPref().getBoolean("save.keepbackup", false)) && tmpFile != null) {
102 Utils.deleteFile(tmpFile);
103 }
104 layer.onPostSaveToFile();
105 } catch (IOException | InvalidPathException e) {
106 Logging.error(e);
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 // re-throw original error
124 throw e;
125 }
126 }
127
128 protected void doSave(File file, OsmDataLayer layer) throws IOException {
129 // create outputstream and wrap it with gzip, xz or bzip, if necessary
130 try (
131 OutputStream out = getOutputStream(file);
132 Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
133 OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, layer.data.getVersion())
134 ) {
135 layer.data.getReadLock().lock();
136 try {
137 w.write(layer.data);
138 } finally {
139 layer.data.getReadLock().unlock();
140 }
141 }
142 }
143}
Note: See TracBrowser for help on using the repository browser.