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.List;
009
010import javax.swing.JOptionPane;
011
012import org.openstreetmap.josm.Main;
013import org.openstreetmap.josm.actions.ExtensionFileFilter;
014import org.openstreetmap.josm.gui.HelpAwareOptionPane;
015import org.openstreetmap.josm.gui.Notification;
016import org.openstreetmap.josm.gui.progress.ProgressMonitor;
017import org.openstreetmap.josm.gui.util.GuiHelper;
018
019/**
020 * Abstract file importer.
021 * @since 1637
022 * @since 10386 (signature)
023 */
024public abstract class FileImporter implements Comparable<FileImporter> {
025
026    /**
027     * The extension file filter used to accept files.
028     */
029    public final ExtensionFileFilter filter;
030
031    private boolean enabled;
032
033    /**
034     * Constructs a new {@code FileImporter} with the given extension file filter.
035     * @param filter The extension file filter
036     */
037    public FileImporter(ExtensionFileFilter filter) {
038        this.filter = filter;
039        this.enabled = true;
040    }
041
042    /**
043     * Determines if this file importer accepts the given file.
044     * @param pathname The file to test
045     * @return {@code true} if this file importer accepts the given file, {@code false} otherwise
046     */
047    public boolean acceptFile(File pathname) {
048        return filter.acceptName(pathname.getName());
049    }
050
051    /**
052     * A batch importer is a file importer that prefers to read multiple files at the same time.
053     * @return {@code true} if this importer is a batch importer
054     */
055    public boolean isBatchImporter() {
056        return false;
057    }
058
059    /**
060     * Needs to be implemented if isBatchImporter() returns false.
061     * @param file file to import
062     * @param progressMonitor progress monitor
063     * @throws IOException if any I/O error occurs
064     * @throws IllegalDataException if invalid data is read
065     */
066    public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
067        throw new IOException(tr("Could not import ''{0}''.", file.getName()));
068    }
069
070    /**
071     * Needs to be implemented if isBatchImporter() returns true.
072     * @param files files to import
073     * @param progressMonitor progress monitor
074     * @throws IOException if any I/O error occurs
075     * @throws IllegalDataException if invalid data is read
076     */
077    public void importData(List<File> files, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
078        throw new IOException(tr("Could not import files."));
079    }
080
081    /**
082     * Wrapper to {@link #importData(File, ProgressMonitor)} to give meaningful output if things go wrong.
083     * @param f data file to import
084     * @param progressMonitor progress monitor
085     * @return true if data import was successful
086     */
087    public boolean importDataHandleExceptions(File f, ProgressMonitor progressMonitor) {
088        try {
089            Main.info("Open file: " + f.getAbsolutePath() + " (" + f.length() + " bytes)");
090            importData(f, progressMonitor);
091            return true;
092        } catch (IllegalDataException e) {
093            Throwable cause = e.getCause();
094            if (cause instanceof ImportCancelException) {
095                displayCancel(cause);
096            } else {
097                displayError(f, e);
098            }
099            return false;
100        } catch (IOException e) {
101            displayError(f, e);
102            return false;
103        }
104    }
105
106    private static void displayError(File f, Exception e) {
107        Main.error(e);
108        HelpAwareOptionPane.showMessageDialogInEDT(
109                Main.parent,
110                tr("<html>Could not read file ''{0}''.<br>Error is:<br>{1}</html>", f.getName(), e.getMessage()),
111                tr("Error"),
112                JOptionPane.ERROR_MESSAGE, null
113        );
114    }
115
116    private static void displayCancel(final Throwable t) {
117        GuiHelper.runInEDTAndWait(() -> {
118            Notification note = new Notification(t.getMessage());
119            note.setIcon(JOptionPane.INFORMATION_MESSAGE);
120            note.setDuration(Notification.TIME_SHORT);
121            note.show();
122        });
123    }
124
125    /**
126     * Wrapper to {@link #importData(List, ProgressMonitor)} to give meaningful output if things go wrong.
127     * @param files data files to import
128     * @param progressMonitor progress monitor
129     * @return true if data import was successful
130     */
131    public boolean importDataHandleExceptions(List<File> files, ProgressMonitor progressMonitor) {
132        try {
133            Main.info("Open "+files.size()+" files");
134            importData(files, progressMonitor);
135            return true;
136        } catch (IOException | IllegalDataException e) {
137            Main.error(e);
138            HelpAwareOptionPane.showMessageDialogInEDT(
139                    Main.parent,
140                    tr("<html>Could not read files.<br>Error is:<br>{0}</html>", e.getMessage()),
141                    tr("Error"),
142                    JOptionPane.ERROR_MESSAGE, null
143            );
144            return false;
145        }
146    }
147
148    /**
149     * If multiple files (with multiple file formats) are selected,
150     * they are opened in the order of their priorities.
151     * Highest priority comes first.
152     * @return priority
153     */
154    public double getPriority() {
155        return 0;
156    }
157
158    @Override
159    public int compareTo(FileImporter other) {
160        return Double.compare(this.getPriority(), other.getPriority());
161    }
162
163    /**
164     * Returns the enabled state of this {@code FileImporter}. When enabled, it is listed and usable in "File-&gt;Open" dialog.
165     * @return true if this {@code FileImporter} is enabled
166     * @since 5459
167     */
168    public final boolean isEnabled() {
169        return enabled;
170    }
171
172    /**
173     * Sets the enabled state of the {@code FileImporter}. When enabled, it is listed and usable in "File-&gt;Open" dialog.
174     * @param enabled true to enable this {@code FileImporter}, false to disable it
175     * @since 5459
176     */
177    public final void setEnabled(boolean enabled) {
178        this.enabled = enabled;
179    }
180}