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