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

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

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

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