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