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}