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

Last change on this file since 9375 was 9230, checked in by Don-vip, 8 years ago

fix javadoc errors/warnings

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