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(new Runnable() {
115                @Override
116                public void run() {
117                    if (canceled)
118                        return;
119                    addLayers();
120                    runPostLoadTasks();
121                }
122            });
123        }
124
125        private void addLayers() {
126            if (layers != null && !layers.isEmpty()) {
127                boolean noMap = Main.map == null;
128                for (Layer l : layers) {
129                    if (canceled)
130                        return;
131                    Main.getLayerManager().addLayer(l);
132                }
133                if (active != null) {
134                    Main.getLayerManager().setActiveLayer(active);
135                }
136                if (noMap) {
137                    Main.map.mapView.scheduleZoomTo(viewport);
138                }
139            }
140        }
141
142        private void runPostLoadTasks() {
143            for (Runnable task : postLoadTasks) {
144                if (canceled)
145                    return;
146                if (task == null) {
147                    continue;
148                }
149                task.run();
150            }
151        }
152
153        @Override
154        protected void realRun() {
155            try {
156                ProgressMonitor monitor = getProgressMonitor();
157                SessionReader reader = new SessionReader();
158                boolean tempFile = false;
159                try {
160                    if (file == null) {
161                        // Download and write entire joz file as a temp file on disk as we need random access later
162                        file = File.createTempFile("session_", ".joz", Utils.getJosmTempDir());
163                        tempFile = true;
164                        Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
165                    }
166                    reader.loadSession(file, zip, monitor);
167                    layers = reader.getLayers();
168                    active = reader.getActive();
169                    postLoadTasks = reader.getPostLoadTasks();
170                    viewport = reader.getViewport();
171                } finally {
172                    if (tempFile) {
173                        Utils.deleteFile(file);
174                        file = null;
175                    }
176                }
177            } catch (IllegalDataException e) {
178                handleException(tr("Data Error"), e);
179            } catch (IOException e) {
180                handleException(tr("IO Error"), e);
181            } catch (RuntimeException e) {
182                cancel();
183                throw e;
184            }
185        }
186
187        private void handleException(String dialogTitle, Exception e) {
188            Main.error(e);
189            HelpAwareOptionPane.showMessageDialogInEDT(
190                    Main.parent,
191                    tr("<html>Could not load session file ''{0}''.<br>Error is:<br>{1}</html>",
192                            uri != null ? uri : file.getName(), e.getMessage()),
193                    dialogTitle,
194                    JOptionPane.ERROR_MESSAGE,
195                    null
196                    );
197            cancel();
198        }
199    }
200}