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

Last change on this file since 8345 was 8308, checked in by Don-vip, 9 years ago

fix potential NPEs and Sonar issues related to serialization

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