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

Last change on this file since 16436 was 16436, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

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