001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.event.ActionEvent;
008import java.io.File;
009import java.io.IOException;
010import java.io.InputStream;
011import java.net.URI;
012import java.nio.file.Files;
013import java.nio.file.StandardCopyOption;
014import java.util.Arrays;
015import java.util.List;
016
017import javax.swing.JFileChooser;
018import javax.swing.JOptionPane;
019import javax.swing.SwingUtilities;
020
021import org.openstreetmap.josm.Main;
022import org.openstreetmap.josm.data.ViewportData;
023import org.openstreetmap.josm.gui.HelpAwareOptionPane;
024import org.openstreetmap.josm.gui.PleaseWaitRunnable;
025import org.openstreetmap.josm.gui.layer.Layer;
026import org.openstreetmap.josm.gui.progress.ProgressMonitor;
027import org.openstreetmap.josm.gui.util.FileFilterAllFiles;
028import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
029import org.openstreetmap.josm.io.IllegalDataException;
030import org.openstreetmap.josm.io.session.SessionImporter;
031import org.openstreetmap.josm.io.session.SessionReader;
032import org.openstreetmap.josm.tools.CheckParameterUtil;
033import org.openstreetmap.josm.tools.Utils;
034
035/**
036 * Loads a JOSM session.
037 * @since 4668
038 */
039public class SessionLoadAction extends DiskAccessAction {
040
041    /**
042     * Constructs a new {@code SessionLoadAction}.
043     */
044    public SessionLoadAction() {
045        super(tr("Load Session"), "open", tr("Load a session from file."), null, true, "load-session", true);
046        putValue("help", ht("/Action/SessionLoad"));
047    }
048
049    @Override
050    public void actionPerformed(ActionEvent e) {
051        AbstractFileChooser fc = createAndOpenFileChooser(true, false, tr("Open session"),
052                Arrays.asList(SessionImporter.FILE_FILTER, FileFilterAllFiles.getInstance()),
053                SessionImporter.FILE_FILTER, JFileChooser.FILES_ONLY, "lastDirectory");
054        if (fc == null)
055            return;
056        File file = fc.getSelectedFile();
057        boolean zip = Utils.hasExtension(file, "joz");
058        Main.worker.submit(new Loader(file, zip));
059    }
060
061    /**
062     * JOSM session loader
063     */
064    public static class Loader extends PleaseWaitRunnable {
065
066        private boolean canceled;
067        private File file;
068        private final URI uri;
069        private final InputStream is;
070        private final boolean zip;
071        private List<Layer> layers;
072        private Layer active;
073        private List<Runnable> postLoadTasks;
074        private ViewportData viewport;
075
076        /**
077         * Constructs a new {@code Loader} for local session file.
078         * @param file The JOSM session file
079         * @param zip {@code true} if the file is a session archive file (*.joz)
080         */
081        public Loader(File file, boolean zip) {
082            super(tr("Loading session ''{0}''", file.getName()));
083            CheckParameterUtil.ensureParameterNotNull(file, "file");
084            this.file = file;
085            this.uri = null;
086            this.is = null;
087            this.zip = zip;
088        }
089
090        /**
091         * Constructs a new {@code Loader} for session file input stream (may be a remote file).
092         * @param is The input stream to session file
093         * @param uri The file URI
094         * @param zip {@code true} if the file is a session archive file (*.joz)
095         * @since 6245
096         */
097        public Loader(InputStream is, URI uri, boolean zip) {
098            super(tr("Loading session ''{0}''", uri));
099            CheckParameterUtil.ensureParameterNotNull(is, "is");
100            CheckParameterUtil.ensureParameterNotNull(uri, "uri");
101            this.file = null;
102            this.uri = uri;
103            this.is = is;
104            this.zip = zip;
105        }
106
107        @Override
108        public void cancel() {
109            canceled = true;
110        }
111
112        @Override
113        protected void finish() {
114            SwingUtilities.invokeLater(() -> {
115                if (canceled)
116                    return;
117                addLayers();
118                runPostLoadTasks();
119            });
120        }
121
122        private void addLayers() {
123            if (layers != null && !layers.isEmpty()) {
124                boolean noMap = Main.map == null;
125                for (Layer l : layers) {
126                    if (canceled)
127                        return;
128                    Main.getLayerManager().addLayer(l);
129                }
130                if (active != null) {
131                    Main.getLayerManager().setActiveLayer(active);
132                }
133                if (noMap) {
134                    Main.map.mapView.scheduleZoomTo(viewport);
135                }
136            }
137        }
138
139        private void runPostLoadTasks() {
140            for (Runnable task : postLoadTasks) {
141                if (canceled)
142                    return;
143                if (task == null) {
144                    continue;
145                }
146                task.run();
147            }
148        }
149
150        @Override
151        protected void realRun() {
152            try {
153                ProgressMonitor monitor = getProgressMonitor();
154                SessionReader reader = new SessionReader();
155                boolean tempFile = false;
156                try {
157                    if (file == null) {
158                        // Download and write entire joz file as a temp file on disk as we need random access later
159                        file = File.createTempFile("session_", ".joz", Utils.getJosmTempDir());
160                        tempFile = true;
161                        Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
162                    }
163                    reader.loadSession(file, zip, monitor);
164                    layers = reader.getLayers();
165                    active = reader.getActive();
166                    postLoadTasks = reader.getPostLoadTasks();
167                    viewport = reader.getViewport();
168                } finally {
169                    if (tempFile) {
170                        Utils.deleteFile(file);
171                        file = null;
172                    }
173                }
174            } catch (IllegalDataException e) {
175                handleException(tr("Data Error"), e);
176            } catch (IOException e) {
177                handleException(tr("IO Error"), e);
178            } catch (RuntimeException e) {
179                cancel();
180                throw e;
181            }
182        }
183
184        private void handleException(String dialogTitle, Exception e) {
185            Main.error(e);
186            HelpAwareOptionPane.showMessageDialogInEDT(
187                    Main.parent,
188                    tr("<html>Could not load session file ''{0}''.<br>Error is:<br>{1}</html>",
189                            uri != null ? uri : file.getName(), e.getMessage()),
190                    dialogTitle,
191                    JOptionPane.ERROR_MESSAGE,
192                    null
193                    );
194            cancel();
195        }
196    }
197}