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

Last change on this file since 13790 was 13204, checked in by Don-vip, 6 years ago

enable new PMD rule AvoidFileStream - see https://pmd.github.io/pmd-6.0.0/pmd_rules_java_performance.html#avoidfilestream / https://bugs.openjdk.java.net/browse/JDK-8080225 for details

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