source: josm/trunk/src/org/openstreetmap/josm/io/session/SessionReader.java@ 19050

Last change on this file since 19050 was 19050, checked in by taylor.smock, 4 weeks ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

  • Property svn:eol-style set to native
File size: 32.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.session;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GraphicsEnvironment;
7import java.io.BufferedInputStream;
8import java.io.File;
9import java.io.FileNotFoundException;
10import java.io.IOException;
11import java.io.InputStream;
12import java.lang.reflect.InvocationTargetException;
13import java.net.URI;
14import java.net.URISyntaxException;
15import java.nio.charset.StandardCharsets;
16import java.nio.file.Files;
17import java.util.AbstractMap.SimpleEntry;
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.Collections;
21import java.util.Enumeration;
22import java.util.HashMap;
23import java.util.List;
24import java.util.Map;
25import java.util.Map.Entry;
26import java.util.TreeMap;
27import java.util.stream.Collectors;
28import java.util.stream.IntStream;
29import java.util.zip.ZipEntry;
30import java.util.zip.ZipException;
31import java.util.zip.ZipFile;
32
33import javax.swing.JOptionPane;
34import javax.swing.SwingUtilities;
35import javax.xml.parsers.ParserConfigurationException;
36
37import org.openstreetmap.josm.data.ViewportData;
38import org.openstreetmap.josm.data.coor.EastNorth;
39import org.openstreetmap.josm.data.coor.ILatLon;
40import org.openstreetmap.josm.data.coor.LatLon;
41import org.openstreetmap.josm.data.projection.Projection;
42import org.openstreetmap.josm.gui.ExceptionDialogUtil;
43import org.openstreetmap.josm.gui.ExtendedDialog;
44import org.openstreetmap.josm.gui.MainApplication;
45import org.openstreetmap.josm.gui.layer.Layer;
46import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
47import org.openstreetmap.josm.gui.progress.ProgressMonitor;
48import org.openstreetmap.josm.gui.util.GuiHelper;
49import org.openstreetmap.josm.io.Compression;
50import org.openstreetmap.josm.io.IllegalDataException;
51import org.openstreetmap.josm.plugins.PluginHandler;
52import org.openstreetmap.josm.tools.CheckParameterUtil;
53import org.openstreetmap.josm.tools.JosmRuntimeException;
54import org.openstreetmap.josm.tools.Logging;
55import org.openstreetmap.josm.tools.MultiMap;
56import org.openstreetmap.josm.tools.Utils;
57import org.openstreetmap.josm.tools.XmlUtils;
58import org.w3c.dom.Document;
59import org.w3c.dom.Element;
60import org.w3c.dom.Node;
61import org.w3c.dom.NodeList;
62import org.xml.sax.SAXException;
63
64/**
65 * Reads a .jos session file and loads the layers in the process.
66 * @since 4668
67 */
68public class SessionReader {
69
70 /**
71 * Data class for projection saved in the session file.
72 */
73 public static class SessionProjectionChoiceData {
74 private final String projectionChoiceId;
75 private final Collection<String> subPreferences;
76
77 /**
78 * Construct a new SessionProjectionChoiceData.
79 * @param projectionChoiceId projection choice id
80 * @param subPreferences parameters for the projection choice
81 */
82 public SessionProjectionChoiceData(String projectionChoiceId, Collection<String> subPreferences) {
83 this.projectionChoiceId = projectionChoiceId;
84 this.subPreferences = subPreferences;
85 }
86
87 /**
88 * Get the projection choice id.
89 * @return the projection choice id
90 */
91 public String getProjectionChoiceId() {
92 return projectionChoiceId;
93 }
94
95 /**
96 * Get the parameters for the projection choice
97 * @return parameters for the projection choice
98 */
99 public Collection<String> getSubPreferences() {
100 return subPreferences;
101 }
102 }
103
104 /**
105 * Data class for viewport saved in the session file.
106 */
107 public static class SessionViewportData {
108 private final LatLon center;
109 private final double meterPerPixel;
110
111 /**
112 * Construct a new SessionViewportData.
113 * @param center the lat/lon coordinates of the screen center
114 * @param meterPerPixel scale in meters per pixel
115 */
116 public SessionViewportData(LatLon center, double meterPerPixel) {
117 CheckParameterUtil.ensureParameterNotNull(center);
118 this.center = center;
119 this.meterPerPixel = meterPerPixel;
120 }
121
122 /**
123 * Get the lat/lon coordinates of the screen center.
124 * @return lat/lon coordinates of the screen center
125 */
126 public LatLon getCenter() {
127 return center;
128 }
129
130 /**
131 * Get the scale in meters per pixel.
132 * @return scale in meters per pixel
133 */
134 public double getScale() {
135 return meterPerPixel;
136 }
137
138 /**
139 * Convert this viewport data to a {@link ViewportData} object (with projected coordinates).
140 * @param proj the projection to convert from lat/lon to east/north
141 * @return the corresponding ViewportData object
142 */
143 public ViewportData getEastNorthViewport(Projection proj) {
144 EastNorth centerEN = proj.latlon2eastNorth(center);
145 // Get a "typical" distance in east/north units that
146 // corresponds to a couple of pixels. Shouldn't be too
147 // large, to keep it within projection bounds and
148 // not too small to avoid rounding errors.
149 double dist = 0.01 * proj.getDefaultZoomInPPD();
150 ILatLon ll1 = proj.eastNorth2latlon(new EastNorth(centerEN.east() - dist, centerEN.north()));
151 ILatLon ll2 = proj.eastNorth2latlon(new EastNorth(centerEN.east() + dist, centerEN.north()));
152 double meterPerEasting = ll1.greatCircleDistance(ll2) / dist / 2;
153 double scale = meterPerPixel / meterPerEasting; // unit: easting per pixel
154 return new ViewportData(centerEN, scale);
155 }
156 }
157
158 private static final Map<String, Class<? extends SessionLayerImporter>> sessionLayerImporters = new HashMap<>();
159
160 private URI sessionFileURI;
161 private boolean zip; // true, if session file is a .joz file; false if it is a .jos file
162 private boolean pluginData; // true, if a plugin restored state from a .joz file. False otherwise.
163 private ZipFile zipFile;
164 private List<Layer> layers = new ArrayList<>();
165 private int active = -1;
166 private final List<Runnable> postLoadTasks = new ArrayList<>();
167 private SessionViewportData viewport;
168 private SessionProjectionChoiceData projectionChoice;
169
170 static {
171 registerSessionLayerImporter("osm-data", OsmDataSessionImporter.class);
172 registerSessionLayerImporter("imagery", ImagerySessionImporter.class);
173 registerSessionLayerImporter("tracks", GpxTracksSessionImporter.class);
174 registerSessionLayerImporter("routes", GpxRoutesSessionImporter.class);
175 registerSessionLayerImporter("geoimage", GeoImageSessionImporter.class);
176 registerSessionLayerImporter("markers", MarkerSessionImporter.class);
177 registerSessionLayerImporter("osm-notes", NoteSessionImporter.class);
178 }
179
180 /**
181 * Register a session layer importer.
182 *
183 * @param layerType layer type
184 * @param importer importer for this layer class
185 */
186 public static void registerSessionLayerImporter(String layerType, Class<? extends SessionLayerImporter> importer) {
187 sessionLayerImporters.put(layerType, importer);
188 }
189
190 /**
191 * Returns the session layer importer for the given layer type.
192 * @param layerType layer type to import
193 * @return session layer importer for the given layer
194 */
195 public static SessionLayerImporter getSessionLayerImporter(String layerType) {
196 Class<? extends SessionLayerImporter> importerClass = sessionLayerImporters.get(layerType);
197 if (importerClass == null)
198 return null;
199 SessionLayerImporter importer;
200 try {
201 importer = importerClass.getConstructor().newInstance();
202 } catch (ReflectiveOperationException e) {
203 throw new JosmRuntimeException(e);
204 }
205 return importer;
206 }
207
208 /**
209 * Returns list of layers that are later added to the mapview.
210 * @return list of layers that are later added to the mapview
211 */
212 public List<Layer> getLayers() {
213 return layers;
214 }
215
216 /**
217 * Returns active layer.
218 * @return active layer, or {@code null} if not set
219 * @since 6271
220 */
221 public Layer getActive() {
222 // layers is in reverse order because of the way TreeMap is built
223 return (active >= 0 && active < layers.size()) ? layers.get(layers.size()-1-active) : null;
224 }
225
226 /**
227 * Returns actions executed in EDT after layers have been added.
228 * @return actions executed in EDT after layers have been added (message dialog, etc.)
229 */
230 public List<Runnable> getPostLoadTasks() {
231 return postLoadTasks;
232 }
233
234 /**
235 * Returns the viewport (map position and scale).
236 * @return the viewport; can be null when no viewport info is found in the file
237 */
238 public SessionViewportData getViewport() {
239 return viewport;
240 }
241
242 /**
243 * Returns the projection choice data.
244 * @return the projection; can be null when no projection info is found in the file
245 */
246 public SessionProjectionChoiceData getProjectionChoice() {
247 return projectionChoice;
248 }
249
250 /**
251 * Returns whether plugins loaded additonal data
252 * @return {@code true} if at least one plugin loaded additional data
253 * @since 18833
254 */
255 public boolean loadedPluginData() {
256 return this.pluginData;
257 }
258
259 /**
260 * A class that provides some context for the individual {@link SessionLayerImporter}
261 * when doing the import.
262 */
263 public class ImportSupport {
264
265 private final String layerName;
266 private final int layerIndex;
267 private final List<LayerDependency> layerDependencies;
268 private Map<Integer, Entry<Layer, Element>> subLayers;
269
270 /**
271 * Path of the file inside the zip archive.
272 * Used as alternative return value for getFile method.
273 */
274 private String inZipPath;
275
276 /**
277 * Constructs a new {@code ImportSupport}.
278 * @param layerName layer name
279 * @param layerIndex layer index
280 * @param layerDependencies layer dependencies
281 */
282 public ImportSupport(String layerName, int layerIndex, List<LayerDependency> layerDependencies) {
283 this.layerName = layerName;
284 this.layerIndex = layerIndex;
285 this.layerDependencies = layerDependencies;
286 }
287
288 /**
289 * Add a task, e.g. a message dialog, that should
290 * be executed in EDT after all layers have been added.
291 * @param task task to run in EDT
292 */
293 public void addPostLayersTask(Runnable task) {
294 postLoadTasks.add(task);
295 }
296
297 /**
298 * Add sub layers
299 * @param idx index
300 * @param layer sub layer
301 * @param el The XML element of the sub layer.
302 * Should contain "index" and "name" attributes.
303 * Can contain "opacity" and "visible" attributes
304 * @since 18466
305 */
306 public void addSubLayer(int idx, Layer layer, Element el) {
307 if (subLayers == null) {
308 subLayers = new HashMap<>();
309 }
310 subLayers.put(idx, new SimpleEntry<>(layer, el));
311 }
312
313 /**
314 * Returns the sub layers
315 * @return the sub layers. Can be null.
316 * @since 18466
317 */
318 public Map<Integer, Entry<Layer, Element>> getSubLayers() {
319 return subLayers;
320 }
321
322 /**
323 * Return an InputStream for a URI from a .jos/.joz file.
324 * <p>
325 * The following forms are supported:
326 * <p>
327 * - absolute file (both .jos and .joz):
328 * "file:///home/user/data.osm"
329 * "file:/home/user/data.osm"
330 * "file:///C:/files/data.osm"
331 * "file:/C:/file/data.osm"
332 * "/home/user/data.osm"
333 * "C:\files\data.osm" (not a URI, but recognized by File constructor on Windows systems)
334 * - standalone .jos files:
335 * - relative uri:
336 * "save/data.osm"
337 * "../project2/data.osm"
338 * - for .joz files:
339 * - file inside zip archive:
340 * "layers/01/data.osm"
341 * - relative to the .joz file:
342 * "../save/data.osm" ("../" steps out of the archive)
343 * @param uriStr URI as string
344 * @return the InputStream
345 *
346 * @throws IOException Thrown when no Stream can be opened for the given URI, e.g. when the linked file has been deleted.
347 */
348 public InputStream getInputStream(String uriStr) throws IOException {
349 File file = getFile(uriStr);
350 if (file != null) {
351 try {
352 return new BufferedInputStream(Compression.getUncompressedFileInputStream(file));
353 } catch (FileNotFoundException e) {
354 throw new IOException(tr("File ''{0}'' does not exist.", file.getPath()), e);
355 }
356 } else if (inZipPath != null) {
357 ZipEntry entry = zipFile.getEntry(inZipPath);
358 if (entry != null) {
359 return zipFile.getInputStream(entry);
360 }
361 }
362 throw new IOException(tr("Unable to locate file ''{0}''.", uriStr));
363 }
364
365 /**
366 * Return a File for a URI from a .jos/.joz file.
367 * <p>
368 * Returns null if the URI points to a file inside the zip archive.
369 * In this case, inZipPath will be set to the corresponding path.
370 * @param uriStr the URI as string
371 * @return the resulting File
372 * @throws IOException if any I/O error occurs
373 */
374 public File getFile(String uriStr) throws IOException {
375 inZipPath = null;
376 try {
377 URI uri = new URI(uriStr);
378 if ("file".equals(uri.getScheme()))
379 // absolute path
380 return new File(uri);
381 else if (uri.getScheme() == null) {
382 // Check if this is an absolute path without 'file:' scheme part.
383 // At this point, (as an exception) platform dependent path separator will be recognized.
384 // (This form is discouraged, only for users that like to copy and paste a path manually.)
385 File file = new File(uriStr);
386 if (file.isAbsolute())
387 return file;
388 else {
389 // for relative paths, only forward slashes are permitted
390 if (isZip()) {
391 if (uri.getPath().startsWith("../")) {
392 // relative to session file - "../" step out of the archive
393 String relPath = uri.getPath().substring(3);
394 return new File(sessionFileURI.resolve(relPath));
395 } else {
396 // file inside zip archive
397 inZipPath = uriStr;
398 return null;
399 }
400 } else
401 return new File(sessionFileURI.resolve(uri));
402 }
403 } else
404 throw new IOException(tr("Unsupported scheme ''{0}'' in URI ''{1}''.", uri.getScheme(), uriStr));
405 } catch (URISyntaxException | IllegalArgumentException e) {
406 throw new IOException(e);
407 }
408 }
409
410 /**
411 * Determines if we are reading from a .joz file.
412 * @return {@code true} if we are reading from a .joz file, {@code false} otherwise
413 */
414 public boolean isZip() {
415 return zip;
416 }
417
418 /**
419 * Name of the layer that is currently imported.
420 * @return layer name
421 */
422 public String getLayerName() {
423 return layerName;
424 }
425
426 /**
427 * Index of the layer that is currently imported.
428 * @return layer index
429 */
430 public int getLayerIndex() {
431 return layerIndex;
432 }
433
434 /**
435 * Dependencies - maps the layer index to the importer of the given
436 * layer. All the dependent importers have loaded completely at this point.
437 * @return layer dependencies
438 */
439 public List<LayerDependency> getLayerDependencies() {
440 return layerDependencies;
441 }
442
443 @Override
444 public String toString() {
445 return "ImportSupport [layerName=" + layerName + ", layerIndex=" + layerIndex + ", layerDependencies="
446 + layerDependencies + ", inZipPath=" + inZipPath + ']';
447 }
448 }
449
450 /**
451 * A dependency of another layer
452 */
453 public static class LayerDependency {
454 private final Integer index;
455 private final Layer layer;
456 private final SessionLayerImporter importer;
457
458 public LayerDependency(Integer index, Layer layer, SessionLayerImporter importer) {
459 this.index = index;
460 this.layer = layer;
461 this.importer = importer;
462 }
463
464 public SessionLayerImporter getImporter() {
465 return importer;
466 }
467
468 public Integer getIndex() {
469 return index;
470 }
471
472 public Layer getLayer() {
473 return layer;
474 }
475 }
476
477 private static void error(String msg) throws IllegalDataException {
478 throw new IllegalDataException(msg);
479 }
480
481 private void parseJos(Document doc, ProgressMonitor progressMonitor) throws IllegalDataException {
482 Element root = doc.getDocumentElement();
483 if (!"josm-session".equals(root.getTagName())) {
484 error(tr("Unexpected root element ''{0}'' in session file", root.getTagName()));
485 }
486 String version = root.getAttribute("version");
487 if (!"0.1".equals(version)) {
488 error(tr("Version ''{0}'' of session file is not supported. Expected: 0.1", version));
489 }
490
491 viewport = readViewportData(root);
492 projectionChoice = readProjectionChoiceData(root);
493
494 Element layersEl = getElementByTagName(root, "layers");
495 if (layersEl == null) return;
496
497 String activeAtt = layersEl.getAttribute("active");
498 try {
499 active = !activeAtt.isEmpty() ? (Integer.parseInt(activeAtt)-1) : -1;
500 } catch (NumberFormatException e) {
501 Logging.warn("Unsupported value for 'active' layer attribute. Ignoring it. Error was: "+e.getMessage());
502 active = -1;
503 }
504
505 MultiMap<Integer, Integer> deps = new MultiMap<>();
506 Map<Integer, Element> elems = new HashMap<>();
507
508 NodeList nodes = layersEl.getChildNodes();
509
510 for (int i = 0; i < nodes.getLength(); ++i) {
511 Node node = nodes.item(i);
512 if (node.getNodeType() == Node.ELEMENT_NODE) {
513 Element e = (Element) node;
514 if ("layer".equals(e.getTagName())) {
515 if (!e.hasAttribute("index")) {
516 error(tr("missing mandatory attribute ''index'' for element ''layer''"));
517 }
518 Integer idx = null;
519 try {
520 idx = Integer.valueOf(e.getAttribute("index"));
521 } catch (NumberFormatException ex) {
522 Logging.warn(ex);
523 }
524 if (idx == null) {
525 error(tr("unexpected format of attribute ''index'' for element ''layer''"));
526 } else if (elems.containsKey(idx)) {
527 error(tr("attribute ''index'' ({0}) for element ''layer'' must be unique", Integer.toString(idx)));
528 }
529 elems.put(idx, e);
530
531 deps.putVoid(idx);
532 String depStr = e.getAttribute("depends");
533 if (!depStr.isEmpty()) {
534 for (String sd : depStr.split(",", -1)) {
535 Integer d = null;
536 try {
537 d = Integer.valueOf(sd);
538 } catch (NumberFormatException ex) {
539 Logging.warn(ex);
540 }
541 if (d != null) {
542 deps.put(idx, d);
543 }
544 }
545 }
546 }
547 }
548 }
549
550 List<Integer> sorted = Utils.topologicalSort(deps);
551 final Map<Integer, Layer> layersMap = new TreeMap<>(Collections.reverseOrder());
552 final Map<Integer, SessionLayerImporter> importers = new HashMap<>();
553
554 progressMonitor.setTicksCount(sorted.size());
555 LAYER: for (int idx: sorted) {
556 Element e = elems.get(idx);
557 if (e == null) {
558 error(tr("missing layer with index {0}", idx));
559 return;
560 } else if (!e.hasAttribute("name")) {
561 error(tr("missing mandatory attribute ''name'' for element ''layer''"));
562 return;
563 }
564 String name = e.getAttribute("name");
565 if (!e.hasAttribute("type")) {
566 error(tr("missing mandatory attribute ''type'' for element ''layer''"));
567 return;
568 }
569 String type = e.getAttribute("type");
570 SessionLayerImporter imp = getSessionLayerImporter(type);
571 if (imp == null && !GraphicsEnvironment.isHeadless()) {
572 CancelOrContinueDialog dialog = new CancelOrContinueDialog();
573 dialog.show(
574 tr("Unable to load layer"),
575 tr("Cannot load layer of type ''{0}'' because no suitable importer was found.", type),
576 JOptionPane.WARNING_MESSAGE
577 );
578 if (dialog.isCancel()) {
579 progressMonitor.cancel();
580 return;
581 } else {
582 continue;
583 }
584 } else if (imp != null) {
585 importers.put(idx, imp);
586 List<LayerDependency> depsImp = new ArrayList<>();
587 for (int d : deps.get(idx)) {
588 SessionLayerImporter dImp = importers.get(d);
589 if (dImp == null) {
590 CancelOrContinueDialog dialog = new CancelOrContinueDialog();
591 dialog.show(
592 tr("Unable to load layer"),
593 tr("Cannot load layer {0} because it depends on layer {1} which has been skipped.", idx, d),
594 JOptionPane.WARNING_MESSAGE
595 );
596 if (dialog.isCancel()) {
597 progressMonitor.cancel();
598 return;
599 } else {
600 continue LAYER;
601 }
602 }
603 depsImp.add(new LayerDependency(d, layersMap.get(d), dImp));
604 }
605 ImportSupport support = new ImportSupport(name, idx, depsImp);
606 Layer layer = null;
607 Exception exception = null;
608 try {
609 layer = imp.load(e, support, progressMonitor.createSubTaskMonitor(1, false));
610 if (layer == null) {
611 throw new IllegalStateException("Importer " + imp + " returned null for " + support);
612 }
613 } catch (IllegalDataException | IllegalArgumentException | IllegalStateException | IOException ex) {
614 exception = ex;
615 }
616 if (exception != null) {
617 Logging.error(exception);
618 if (!GraphicsEnvironment.isHeadless()) {
619 CancelOrContinueDialog dialog = new CancelOrContinueDialog();
620 dialog.show(
621 tr("Error loading layer"),
622 tr("<html>Could not load layer {0} ''{1}''.<br>Error is:<br>{2}</html>", idx,
623 Utils.escapeReservedCharactersHTML(name),
624 Utils.escapeReservedCharactersHTML(exception.getMessage())),
625 JOptionPane.ERROR_MESSAGE
626 );
627 if (dialog.isCancel()) {
628 progressMonitor.cancel();
629 return;
630 } else {
631 continue;
632 }
633 }
634 }
635
636 layersMap.put(idx, layer);
637 setLayerAttributes(layer, e);
638
639 if (support.getSubLayers() != null) {
640 support.getSubLayers().forEach((Integer markerIndex, Entry<Layer, Element> entry) -> {
641 Layer subLayer = entry.getKey();
642 Element subElement = entry.getValue();
643
644 layersMap.put(markerIndex, subLayer);
645 setLayerAttributes(subLayer, subElement);
646 });
647 }
648
649 }
650 if (progressMonitor.isCanceled())
651 return;
652 progressMonitor.worked(1);
653 }
654
655
656 layers = new ArrayList<>();
657 for (Entry<Integer, Layer> entry : layersMap.entrySet()) {
658 Layer layer = entry.getValue();
659 if (layer != null) {
660 layers.add(layer);
661 }
662 }
663 }
664
665 private static void setLayerAttributes(Layer layer, Element e) {
666 if (layer == null)
667 return;
668
669 if (e.hasAttribute("name")) {
670 layer.setName(e.getAttribute("name"));
671 }
672 if (e.hasAttribute("visible")) {
673 layer.setVisible(Boolean.parseBoolean(e.getAttribute("visible")));
674 }
675 if (e.hasAttribute("opacity")) {
676 try {
677 double opacity = Double.parseDouble(e.getAttribute("opacity"));
678 layer.setOpacity(opacity);
679 } catch (NumberFormatException ex) {
680 Logging.warn(ex);
681 }
682 }
683 }
684
685 private static SessionViewportData readViewportData(Element root) {
686 Element viewportEl = getElementByTagName(root, "viewport");
687 if (viewportEl == null) return null;
688 LatLon center = null;
689 Element centerEl = getElementByTagName(viewportEl, "center");
690 if (centerEl == null || !centerEl.hasAttribute("lat") || !centerEl.hasAttribute("lon")) return null;
691 try {
692 center = new LatLon(Double.parseDouble(centerEl.getAttribute("lat")),
693 Double.parseDouble(centerEl.getAttribute("lon")));
694 } catch (NumberFormatException ex) {
695 Logging.warn(ex);
696 }
697 if (center == null) return null;
698 Element scaleEl = getElementByTagName(viewportEl, "scale");
699 if (scaleEl == null || !scaleEl.hasAttribute("meter-per-pixel")) return null;
700 try {
701 double scale = Double.parseDouble(scaleEl.getAttribute("meter-per-pixel"));
702 return new SessionViewportData(center, scale);
703 } catch (NumberFormatException ex) {
704 Logging.warn(ex);
705 return null;
706 }
707 }
708
709 private static SessionProjectionChoiceData readProjectionChoiceData(Element root) {
710 Element projectionEl = getElementByTagName(root, "projection");
711 if (projectionEl == null) return null;
712 Element projectionChoiceEl = getElementByTagName(projectionEl, "projection-choice");
713 if (projectionChoiceEl == null) return null;
714 Element idEl = getElementByTagName(projectionChoiceEl, "id");
715 if (idEl == null) return null;
716 String id = idEl.getTextContent();
717 Element parametersEl = getElementByTagName(projectionChoiceEl, "parameters");
718 if (parametersEl == null) return null;
719 NodeList paramNl = parametersEl.getElementsByTagName("param");
720 int length = paramNl.getLength();
721 Collection<String> parameters = IntStream.range(0, length)
722 .mapToObj(i -> (Element) paramNl.item(i)).map(Node::getTextContent)
723 .collect(Collectors.toList());
724 return new SessionProjectionChoiceData(id, parameters);
725 }
726
727 /**
728 * Show Dialog when there is an error for one layer.
729 * Ask the user whether to cancel the complete session loading or just to skip this layer.
730 * <p>
731 * This is expected to run in a worker thread (PleaseWaitRunnable), so invokeAndWait is
732 * needed to block the current thread and wait for the result of the modal dialog from EDT.
733 */
734 private static final class CancelOrContinueDialog {
735
736 private boolean cancel;
737
738 void show(final String title, final String message, final int icon) {
739 try {
740 SwingUtilities.invokeAndWait(() -> {
741 ExtendedDialog dlg = new ExtendedDialog(
742 MainApplication.getMainFrame(),
743 title,
744 tr("Cancel"), tr("Skip layer and continue"))
745 .setButtonIcons("cancel", "dialogs/next")
746 .setIcon(icon)
747 .setContent(message);
748 cancel = dlg.showDialog().getValue() != 2;
749 });
750 } catch (InvocationTargetException | InterruptedException ex) {
751 throw new JosmRuntimeException(ex);
752 }
753 }
754
755 public boolean isCancel() {
756 return cancel;
757 }
758 }
759
760 private void loadPluginData() {
761 if (!zip) {
762 return;
763 }
764 for (PluginSessionImporter importer : PluginHandler.load(PluginSessionImporter.class)) {
765 try {
766 this.pluginData |= importer.readZipFile(zipFile);
767 } catch (IOException ioException) {
768 GuiHelper.runInEDT(() -> ExceptionDialogUtil.explainException(ioException));
769 }
770 }
771 }
772
773 /**
774 * Loads session from the given file.
775 * @param sessionFile session file to load
776 * @param zip {@code true} if it's a zipped session (.joz)
777 * @param progressMonitor progress monitor
778 * @throws IllegalDataException if invalid data is detected
779 * @throws IOException if any I/O error occurs
780 */
781 public void loadSession(File sessionFile, boolean zip, ProgressMonitor progressMonitor) throws IllegalDataException, IOException {
782 try (InputStream josIS = createInputStream(sessionFile, zip)) {
783 loadSession(josIS, sessionFile.toURI(), zip, progressMonitor);
784 this.postLoadTasks.add(this::loadPluginData);
785 }
786 }
787
788 private InputStream createInputStream(File sessionFile, boolean zip) throws IOException, IllegalDataException {
789 if (zip) {
790 try {
791 zipFile = new ZipFile(sessionFile, StandardCharsets.UTF_8);
792 return getZipInputStream(zipFile);
793 } catch (ZipException ex) {
794 throw new IOException(ex);
795 }
796 } else {
797 return Files.newInputStream(sessionFile.toPath());
798 }
799 }
800
801 private static InputStream getZipInputStream(ZipFile zipFile) throws IOException, IllegalDataException {
802 ZipEntry josEntry = null;
803 Enumeration<? extends ZipEntry> entries = zipFile.entries();
804 while (entries.hasMoreElements()) {
805 ZipEntry entry = entries.nextElement();
806 if (Utils.hasExtension(entry.getName(), "jos")) {
807 josEntry = entry;
808 break;
809 }
810 }
811 if (josEntry == null) {
812 error(tr("expected .jos file inside .joz archive"));
813 }
814 return zipFile.getInputStream(josEntry);
815 }
816
817 /**
818 * Loads session from the given input stream.
819 * @param josIS session stream to load
820 * @param zip {@code true} if it's a zipped session (.joz)
821 * @param sessionFileURI URI of the underlying session file
822 * @param progressMonitor progress monitor
823 * @throws IllegalDataException if invalid data is detected
824 * @throws IOException if any I/O error occurs
825 * @since 15070
826 */
827 public void loadSession(InputStream josIS, URI sessionFileURI, boolean zip, ProgressMonitor progressMonitor)
828 throws IOException, IllegalDataException {
829
830 this.sessionFileURI = sessionFileURI;
831 this.zip = zip;
832
833 try {
834 parseJos(XmlUtils.parseSafeDOM(josIS), progressMonitor != null ? progressMonitor : NullProgressMonitor.INSTANCE);
835 } catch (SAXException e) {
836 throw new IllegalDataException(e);
837 } catch (ParserConfigurationException e) {
838 throw new IOException(e);
839 }
840 }
841
842 private static Element getElementByTagName(Element root, String name) {
843 NodeList els = root.getElementsByTagName(name);
844 return els.getLength() > 0 ? (Element) els.item(0) : null;
845 }
846}
Note: See TracBrowser for help on using the repository browser.