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