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

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

sonar - remove unnecessary fully qualified names

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