001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io.importexport;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.File;
007import java.io.IOException;
008import java.util.ArrayList;
009import java.util.Arrays;
010import java.util.HashSet;
011import java.util.List;
012import java.util.Set;
013
014import org.openstreetmap.josm.actions.ExtensionFileFilter;
015import org.openstreetmap.josm.gui.layer.GpxLayer;
016import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
017import org.openstreetmap.josm.gui.progress.ProgressMonitor;
018import org.openstreetmap.josm.io.IllegalDataException;
019
020/**
021 * File importer allowing to import geottaged images (*.jpg files).
022 *
023 */
024public class JpgImporter extends FileImporter {
025    private GpxLayer gpx;
026
027    /**
028     * The default file filter (only *.jpg files).
029     */
030    public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter(
031            "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg)");
032
033    /**
034     * An alternate file filter that also includes folders.
035     * @since 5438
036     */
037    public static final ExtensionFileFilter FILE_FILTER_WITH_FOLDERS = new ExtensionFileFilter(
038            "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg, "+ tr("folder")+')');
039
040    /**
041     * Constructs a new {@code JpgImporter}.
042     */
043    public JpgImporter() {
044        this(false);
045    }
046
047    /**
048     * Constructs a new {@code JpgImporter} with folders selection, if wanted.
049     * @param includeFolders If true, includes folders in the file filter
050     * @since 5438
051     */
052    public JpgImporter(boolean includeFolders) {
053        super(includeFolders ? FILE_FILTER_WITH_FOLDERS : FILE_FILTER);
054    }
055
056    /**
057     * Constructs a new {@code JpgImporter} for the given GPX layer. Folders selection is allowed.
058     * @param gpx The GPX layer
059     */
060    public JpgImporter(GpxLayer gpx) {
061        this(true);
062        this.gpx = gpx;
063    }
064
065    @Override
066    public boolean acceptFile(File pathname) {
067        return super.acceptFile(pathname) || pathname.isDirectory();
068    }
069
070    @Override
071    public void importData(List<File> sel, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
072        progressMonitor.beginTask(tr("Looking for image files"), 1);
073        try {
074            List<File> files = new ArrayList<>();
075            Set<String> visitedDirs = new HashSet<>();
076            addRecursiveFiles(files, visitedDirs, sel, progressMonitor.createSubTaskMonitor(1, true));
077
078            if (progressMonitor.isCanceled())
079                return;
080
081            if (files.isEmpty())
082                throw new IOException(tr("No image files found."));
083
084            GeoImageLayer.create(files, gpx);
085        } finally {
086            progressMonitor.finishTask();
087        }
088    }
089
090    static void addRecursiveFiles(List<File> files, Set<String> visitedDirs, List<File> sel, ProgressMonitor progressMonitor)
091            throws IOException {
092
093        if (progressMonitor.isCanceled())
094            return;
095
096        progressMonitor.beginTask(null, sel.size());
097        try {
098            for (File f : sel) {
099                if (f.isDirectory()) {
100                    if (visitedDirs.add(f.getCanonicalPath())) { // Do not loop over symlinks
101                        File[] dirFiles = f.listFiles(); // Can be null for some strange directories (like lost+found)
102                        if (dirFiles != null) {
103                            addRecursiveFiles(files, visitedDirs, Arrays.asList(dirFiles), progressMonitor.createSubTaskMonitor(1, true));
104                        }
105                    } else {
106                        progressMonitor.worked(1);
107                    }
108                } else {
109                    if (FILE_FILTER.accept(f)) {
110                        files.add(f);
111                    }
112                    progressMonitor.worked(1);
113                }
114            }
115        } finally {
116            progressMonitor.finishTask();
117        }
118    }
119
120    @Override
121    public boolean isBatchImporter() {
122        return true;
123    }
124
125    /**
126     * Needs to be the last, to avoid problems.
127     */
128    @Override
129    public double getPriority() {
130        return -1000;
131    }
132}