001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.io;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.io.File;
007    import java.io.IOException;
008    import java.util.ArrayList;
009    import java.util.Arrays;
010    import java.util.HashSet;
011    import java.util.List;
012    import java.util.Set;
013    
014    import org.openstreetmap.josm.actions.ExtensionFileFilter;
015    import org.openstreetmap.josm.gui.layer.GpxLayer;
016    import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
017    import org.openstreetmap.josm.gui.progress.ProgressMonitor;
018    
019    /**
020     * File importer allowing to import geottaged images (*.jpg files).
021     *
022     */
023    public 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    }