source: josm/trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java@ 14693

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

fix #16935 - simplify/cleanup help topics of ToggleDialog/ToggleDialogAction

  • Property svn:eol-style set to native
File size: 16.4 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[1146]2package org.openstreetmap.josm.actions;
3
[2563]4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
[1146]5import static org.openstreetmap.josm.tools.I18n.tr;
[2842]6import static org.openstreetmap.josm.tools.I18n.trn;
[1146]7
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
[4996]10import java.io.BufferedReader;
[1146]11import java.io.File;
12import java.io.IOException;
[7082]13import java.nio.charset.StandardCharsets;
[7315]14import java.nio.file.Files;
[2702]15import java.util.ArrayList;
[2047]16import java.util.Arrays;
[2980]17import java.util.Collection;
[2702]18import java.util.Collections;
[3924]19import java.util.HashSet;
[3710]20import java.util.LinkedHashSet;
[2980]21import java.util.LinkedList;
[2047]22import java.util.List;
[4247]23import java.util.Set;
[11986]24import java.util.concurrent.Future;
[4996]25import java.util.regex.Matcher;
26import java.util.regex.Pattern;
[10212]27import java.util.regex.PatternSyntaxException;
[1146]28
29import javax.swing.JOptionPane;
[2994]30import javax.swing.SwingUtilities;
[2702]31import javax.swing.filechooser.FileFilter;
[1146]32
[12891]33import org.openstreetmap.josm.data.PreferencesUtils;
[2980]34import org.openstreetmap.josm.gui.HelpAwareOptionPane;
[12630]35import org.openstreetmap.josm.gui.MainApplication;
36import org.openstreetmap.josm.gui.MapFrame;
[13727]37import org.openstreetmap.josm.gui.Notification;
[2047]38import org.openstreetmap.josm.gui.PleaseWaitRunnable;
[12671]39import org.openstreetmap.josm.gui.io.importexport.AllFormatsImporter;
40import org.openstreetmap.josm.gui.io.importexport.FileImporter;
[7578]41import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
[2047]42import org.openstreetmap.josm.io.OsmTransferException;
[12846]43import org.openstreetmap.josm.spi.preferences.Config;
[12620]44import org.openstreetmap.josm.tools.Logging;
[2702]45import org.openstreetmap.josm.tools.MultiMap;
[14138]46import org.openstreetmap.josm.tools.PlatformManager;
[1146]47import org.openstreetmap.josm.tools.Shortcut;
[11848]48import org.openstreetmap.josm.tools.Utils;
[2047]49import org.xml.sax.SAXException;
[1146]50
51/**
[7453]52 * Open a file chooser dialog and select a file to import.
[1146]53 *
54 * @author imi
[7453]55 * @since 1146
[1146]56 */
57public class OpenFileAction extends DiskAccessAction {
58
[5874]59 /**
60 * The {@link ExtensionFileFilter} matching .url files
61 */
[7859]62 public static final ExtensionFileFilter URL_FILE_FILTER = new ExtensionFileFilter("url", "url", tr("URL Files") + " (*.url)");
[4996]63
[1169]64 /**
65 * Create an open action. The name is "Open a file".
66 */
67 public OpenFileAction() {
[1212]68 super(tr("Open..."), "open", tr("Open a file."),
[4982]69 Shortcut.registerShortcut("system:open", tr("File: {0}", tr("Open...")), KeyEvent.VK_O, Shortcut.CTRL));
[14397]70 setHelpId(ht("/Action/Open"));
[1169]71 }
[1146]72
[6084]73 @Override
[1169]74 public void actionPerformed(ActionEvent e) {
[7578]75 AbstractFileChooser fc = createAndOpenFileChooser(true, true, null);
[1169]76 if (fc == null)
77 return;
78 File[] files = fc.getSelectedFiles();
[2702]79 OpenFileTask task = new OpenFileTask(Arrays.asList(files), fc.getFileFilter());
[3710]80 task.setRecordHistory(true);
[12634]81 MainApplication.worker.submit(task);
[1169]82 }
[1146]83
[1879]84 @Override
85 protected void updateEnabledState() {
[7026]86 setEnabled(true);
[1879]87 }
[2047]88
[2798]89 /**
90 * Open a list of files. The complete list will be passed to batch importers.
[9230]91 * Filenames will not be saved in history.
[2798]92 * @param fileList A list of files
[11986]93 * @return the future task
94 * @since 11986 (return task)
[2798]95 */
[11986]96 public static Future<?> openFiles(List<File> fileList) {
97 return openFiles(fileList, false);
[3924]98 }
99
[9230]100 /**
101 * Open a list of files. The complete list will be passed to batch importers.
102 * @param fileList A list of files
103 * @param recordHistory {@code true} to save filename in history (default: false)
[11986]104 * @return the future task
105 * @since 11986 (return task)
[9230]106 */
[11986]107 public static Future<?> openFiles(List<File> fileList, boolean recordHistory) {
[2798]108 OpenFileTask task = new OpenFileTask(fileList, null);
[3924]109 task.setRecordHistory(recordHistory);
[12634]110 return MainApplication.worker.submit(task);
[2703]111 }
112
[9230]113 /**
114 * Task to open files.
115 */
[6889]116 public static class OpenFileTask extends PleaseWaitRunnable {
[7453]117 private final List<File> files;
118 private final List<File> successfullyOpenedFiles = new ArrayList<>();
119 private final Set<String> fileHistory = new LinkedHashSet<>();
120 private final Set<String> failedAll = new HashSet<>();
121 private final FileFilter fileFilter;
[4310]122 private boolean canceled;
[8840]123 private boolean recordHistory;
[2047]124
[9230]125 /**
126 * Constructs a new {@code OpenFileTask}.
127 * @param files files to open
128 * @param fileFilter file filter
129 * @param title message for the user
130 */
[8202]131 public OpenFileTask(final List<File> files, final FileFilter fileFilter, final String title) {
[3679]132 super(title, false /* don't ignore exception */);
[2702]133 this.fileFilter = fileFilter;
[8202]134 this.files = new ArrayList<>(files.size());
135 for (final File file : files) {
136 if (file.exists()) {
[14138]137 this.files.add(PlatformManager.getPlatform().resolveFileLink(file));
[8705]138 } else if (file.getParentFile() != null) {
[8202]139 // try to guess an extension using the specified fileFilter
[10601]140 final File[] matchingFiles = file.getParentFile().listFiles((dir, name) ->
141 name.startsWith(file.getName()) && fileFilter != null && fileFilter.accept(new File(dir, name)));
[8308]142 if (matchingFiles != null && matchingFiles.length == 1) {
[8202]143 // use the unique match as filename
144 this.files.add(matchingFiles[0]);
145 } else {
146 // add original filename for error reporting later on
147 this.files.add(file);
148 }
[13727]149 } else {
150 String message = tr("Unable to locate file ''{0}''.", file.getPath());
151 Logging.warn(message);
152 new Notification(message).show();
[8202]153 }
154 }
[2047]155 }
[3679]156
[9230]157 /**
158 * Constructs a new {@code OpenFileTask}.
159 * @param files files to open
160 * @param fileFilter file filter
161 */
[3679]162 public OpenFileTask(List<File> files, FileFilter fileFilter) {
163 this(files, fileFilter, tr("Opening files"));
164 }
165
[3710]166 /**
[9230]167 * Sets whether to save filename in history (for list of recently opened files).
168 * @param recordHistory {@code true} to save filename in history (default: false)
[3710]169 */
170 public void setRecordHistory(boolean recordHistory) {
171 this.recordHistory = recordHistory;
172 }
173
[9230]174 /**
175 * Determines if filename must be saved in history (for list of recently opened files).
176 * @return {@code true} if filename must be saved in history
177 */
[3710]178 public boolean isRecordHistory() {
179 return recordHistory;
180 }
181
[2047]182 @Override
183 protected void cancel() {
[4310]184 this.canceled = true;
[2047]185 }
186
187 @Override
188 protected void finish() {
[12630]189 MapFrame map = MainApplication.getMap();
190 if (map != null) {
191 map.repaint();
[9707]192 }
[2047]193 }
194
[2980]195 protected void alertFilesNotMatchingWithImporter(Collection<File> files, FileImporter importer) {
[10242]196 final StringBuilder msg = new StringBuilder(128).append("<html>").append(
197 trn("Cannot open {0} file with the file importer ''{1}''.",
198 "Cannot open {0} files with the file importer ''{1}''.",
199 files.size(),
200 files.size(),
[11848]201 Utils.escapeReservedCharactersHTML(importer.filter.getDescription())
[2980]202 )
[8390]203 ).append("<br><ul>");
[2980]204 for (File f: files) {
205 msg.append("<li>").append(f.getAbsolutePath()).append("</li>");
206 }
[10242]207 msg.append("</ul></html>");
[2980]208
[3501]209 HelpAwareOptionPane.showMessageDialogInEDT(
[14153]210 MainApplication.getMainFrame(),
[3501]211 msg.toString(),
212 tr("Warning"),
213 JOptionPane.WARNING_MESSAGE,
[9972]214 ht("/Action/Open#ImporterCantImportFiles")
[3501]215 );
[2980]216 }
217
218 protected void alertFilesWithUnknownImporter(Collection<File> files) {
[10242]219 final StringBuilder msg = new StringBuilder(128).append("<html>").append(
220 trn("Cannot open {0} file because file does not exist or no suitable file importer is available.",
221 "Cannot open {0} files because files do not exist or no suitable file importer is available.",
222 files.size(),
223 files.size()
[2980]224 )
[8390]225 ).append("<br><ul>");
[2980]226 for (File f: files) {
[8379]227 msg.append("<li>").append(f.getAbsolutePath()).append(" (<i>")
228 .append(f.exists() ? tr("no importer") : tr("does not exist"))
229 .append("</i>)</li>");
[2980]230 }
[10242]231 msg.append("</ul></html>");
[3530]232
[3501]233 HelpAwareOptionPane.showMessageDialogInEDT(
[14153]234 MainApplication.getMainFrame(),
[3501]235 msg.toString(),
236 tr("Warning"),
237 JOptionPane.WARNING_MESSAGE,
[9972]238 ht("/Action/Open#MissingImporterForFiles")
[3501]239 );
[2980]240 }
241
[2047]242 @Override
243 protected void realRun() throws SAXException, IOException, OsmTransferException {
244 if (files == null || files.isEmpty()) return;
[2702]245
246 /**
247 * Find the importer with the chosen file filter
248 */
249 FileImporter chosenImporter = null;
[7865]250 if (fileFilter != null) {
[10407]251 for (FileImporter importer : ExtensionFileFilter.getImporters()) {
[7865]252 if (fileFilter.equals(importer.filter)) {
253 chosenImporter = importer;
254 }
[2702]255 }
256 }
257 /**
[3710]258 * If the filter hasn't been changed in the dialog, chosenImporter is null now.
259 * When the filter has been set explicitly to AllFormatsImporter, treat this the same.
[2702]260 */
261 if (chosenImporter instanceof AllFormatsImporter) {
262 chosenImporter = null;
263 }
[2851]264 getProgressMonitor().setTicksCount(files.size());
[2702]265
[2980]266 if (chosenImporter != null) {
[4247]267 // The importer was explicitly chosen, so use it.
[7005]268 List<File> filesNotMatchingWithImporter = new LinkedList<>();
269 List<File> filesMatchingWithImporter = new LinkedList<>();
[2994]270 for (final File f : files) {
[2702]271 if (!chosenImporter.acceptFile(f)) {
272 if (f.isDirectory()) {
[14153]273 SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(MainApplication.getMainFrame(), tr(
[10601]274 "<html>Cannot open directory ''{0}''.<br>Please select a file.</html>",
275 f.getAbsolutePath()), tr("Open file"), JOptionPane.ERROR_MESSAGE));
[10242]276 // TODO when changing to Java 6: Don't cancel the task here but use different modality. (Currently 2 dialogs
[2994]277 // would block each other.)
278 return;
[2980]279 } else {
280 filesNotMatchingWithImporter.add(f);
281 }
282 } else {
283 filesMatchingWithImporter.add(f);
[2702]284 }
285 }
[2980]286
287 if (!filesNotMatchingWithImporter.isEmpty()) {
288 alertFilesNotMatchingWithImporter(filesNotMatchingWithImporter, chosenImporter);
289 }
[2994]290 if (!filesMatchingWithImporter.isEmpty()) {
[2980]291 importData(chosenImporter, filesMatchingWithImporter);
292 }
293 } else {
294 // find appropriate importer
[7005]295 MultiMap<FileImporter, File> importerMap = new MultiMap<>();
296 List<File> filesWithUnknownImporter = new LinkedList<>();
297 List<File> urlFiles = new LinkedList<>();
[2980]298 FILES: for (File f : files) {
[10407]299 for (FileImporter importer : ExtensionFileFilter.getImporters()) {
[2980]300 if (importer.acceptFile(f)) {
[3924]301 importerMap.put(importer, f);
[2980]302 continue FILES;
[2702]303 }
304 }
[7859]305 if (URL_FILE_FILTER.accept(f)) {
[4996]306 urlFiles.add(f);
307 } else {
308 filesWithUnknownImporter.add(f);
309 }
[2980]310 }
311 if (!filesWithUnknownImporter.isEmpty()) {
312 alertFilesWithUnknownImporter(filesWithUnknownImporter);
313 }
[7005]314 List<FileImporter> importers = new ArrayList<>(importerMap.keySet());
[3924]315 Collections.sort(importers);
316 Collections.reverse(importers);
[3710]317
[3924]318 for (FileImporter importer : importers) {
[7453]319 importData(importer, new ArrayList<>(importerMap.get(importer)));
[2702]320 }
[6069]321
[14201]322 Pattern urlPattern = Pattern.compile(".*(https?://.*)");
[4996]323 for (File urlFile: urlFiles) {
[7315]324 try (BufferedReader reader = Files.newBufferedReader(urlFile.toPath(), StandardCharsets.UTF_8)) {
[4996]325 String line;
326 while ((line = reader.readLine()) != null) {
[14201]327 Matcher m = urlPattern.matcher(line);
[4996]328 if (m.matches()) {
329 String url = m.group(1);
[12643]330 MainApplication.getMenu().openLocation.openUrl(false, url);
[4996]331 }
332 }
[10212]333 } catch (IOException | PatternSyntaxException | IllegalStateException | IndexOutOfBoundsException e) {
[12620]334 Logging.error(e);
[4996]335 }
336 }
[7453]337 }
[3710]338
[7453]339 if (recordHistory) {
[12846]340 Collection<String> oldFileHistory = Config.getPref().getList("file-open.history");
[7453]341 fileHistory.addAll(oldFileHistory);
342 // remove the files which failed to load from the list
343 fileHistory.removeAll(failedAll);
[12846]344 int maxsize = Math.max(0, Config.getPref().getInt("file-open.history.max-size", 15));
[12894]345 PreferencesUtils.putListBounded(Config.getPref(), "file-open.history", maxsize, new ArrayList<>(fileHistory));
[2702]346 }
347 }
348
[9230]349 /**
350 * Import data files with the given importer.
351 * @param importer file importer
352 * @param files data files to import
353 */
[2702]354 public void importData(FileImporter importer, List<File> files) {
355 if (importer.isBatchImporter()) {
[4310]356 if (canceled) return;
[4393]357 String msg = trn("Opening {0} file...", "Opening {0} files...", files.size(), files.size());
[2980]358 getProgressMonitor().setCustomText(msg);
[2702]359 getProgressMonitor().indeterminateSubTask(msg);
[3679]360 if (importer.importDataHandleExceptions(files, getProgressMonitor().createSubTaskMonitor(files.size(), false))) {
361 successfullyOpenedFiles.addAll(files);
362 }
[2702]363 } else {
364 for (File f : files) {
[4310]365 if (canceled) return;
[2702]366 getProgressMonitor().indeterminateSubTask(tr("Opening file ''{0}'' ...", f.getAbsolutePath()));
[3679]367 if (importer.importDataHandleExceptions(f, getProgressMonitor().createSubTaskMonitor(1, false))) {
368 successfullyOpenedFiles.add(f);
369 }
[2702]370 }
[2047]371 }
[7453]372 if (recordHistory && !importer.isBatchImporter()) {
373 for (File f : files) {
374 try {
375 if (successfullyOpenedFiles.contains(f)) {
376 fileHistory.add(f.getCanonicalPath());
377 } else {
378 failedAll.add(f.getCanonicalPath());
379 }
380 } catch (IOException e) {
[12620]381 Logging.warn(e);
[7453]382 }
383 }
384 }
[2047]385 }
[3679]386
[5874]387 /**
388 * Replies the list of files that have been successfully opened.
389 * @return The list of files that have been successfully opened.
390 */
[3679]391 public List<File> getSuccessfullyOpenedFiles() {
392 return successfullyOpenedFiles;
393 }
[2047]394 }
[2512]395}
Note: See TracBrowser for help on using the repository browser.