a0d301f0058c5784d3a3fc9293f1bad49e5e0d76
[debian/jabref.git] / src / java / net / sf / jabref / external / DownloadExternalFile.java
1 package net.sf.jabref.external;
2
3 import net.sf.jabref.*;
4 import net.sf.jabref.gui.FileListEditor;
5 import net.sf.jabref.gui.FileListEntryEditor;
6 import net.sf.jabref.gui.FileListEntry;
7 import net.sf.jabref.net.URLDownload;
8
9 import javax.swing.*;
10 import java.io.File;
11 import java.io.IOException;
12 import java.net.URL;
13 import java.net.MalformedURLException;
14
15 /**
16  * This class handles the download of an external file. Typically called when the user clicks
17  * the "Download" button in a FileListEditor shown in an EntryEditor.
18  * <p/>
19  * The FileListEditor constructs the DownloadExternalFile instance, then calls the download()
20  * method passing a reference to itself as a callback. The download() method asks for the URL,
21  * then starts the download. When the download is completed, it calls the downloadCompleted()
22  * method on the callback FileListEditor, which then needs to take care of linking to the file.
23  * The local filename is passed as an argument to the downloadCompleted() method.
24  * <p/>
25  * If the download is cancelled, or failed, the user is informed. The callback is never called.
26  */
27 public class DownloadExternalFile {
28     private JabRefFrame frame;
29     private JDialog dialog;
30     private MetaData metaData;
31     private String bibtexKey;
32     private FileListEntryEditor editor;
33     private boolean downloadFinished = false;
34
35     public DownloadExternalFile(JabRefFrame frame, MetaData metaData, String bibtexKey) {
36
37         this.frame = frame;
38         this.metaData = metaData;
39         this.bibtexKey = bibtexKey;
40     }
41
42     /**
43      * Start a download.
44      *
45      * @param callback The object to which the filename should be reported when download
46      *                 is complete.
47      */
48     public void download(final DownloadCallback callback) throws IOException {
49
50         final String res = JOptionPane.showInputDialog(frame,
51                 Globals.lang("Enter URL to download"));
52
53         if (res == null || res.trim().length() == 0)
54             return;
55
56         // First of all, start the download itself in the background to a temporary file:
57         final File tmp = File.createTempFile("jabref_download", "tmp");
58         tmp.deleteOnExit();
59         (new Thread() {
60             public void run() {
61
62                 try {
63
64                     URL url = new URL(res);
65                     URLDownload udl = new URLDownload(frame, url, tmp);
66                     try {
67                         udl.download();
68                     } catch (IOException e2) {
69                         JOptionPane.showMessageDialog(frame, Globals.lang("Invalid URL: "
70                                 + e2.getMessage()), Globals.lang("Download file"),
71                                 JOptionPane.ERROR_MESSAGE);
72                         Globals.logger("Error while downloading " + url.toString());
73                         return;
74                     }
75
76                     // Download finished: call the method that stops the progress bar etc.:
77                     SwingUtilities.invokeLater(new Runnable() {
78                         public void run() {
79                             downloadFinished();
80                         }
81                     });
82
83
84                 } catch (MalformedURLException e1) {
85                     JOptionPane.showMessageDialog(frame, Globals.lang("Invalid URL"), Globals
86                             .lang("Download file"), JOptionPane.ERROR_MESSAGE);
87                 }
88             }
89         }).start();
90
91         // Then, while the download is proceeding, let the user choose the details of the file:
92         String suffix = getSuffix(res);
93         String suggestedName = bibtexKey != null ? getSuggestedFileName(res, suffix) : "";
94         final String directory = getFileDirectory(res);
95         File file = new File(new File(directory), suggestedName);
96         FileListEntry entry = new FileListEntry("", bibtexKey != null ? file.getPath() : "",
97                 Globals.prefs.getExternalFileTypeByExt(suffix));
98         editor = new FileListEntryEditor(frame, entry, true, metaData);
99         editor.getProgressBar().setIndeterminate(true);
100         editor.setOkEnabled(false);
101         editor.setExternalConfirm(new ConfirmCloseFileListEntryEditor() {
102             public boolean confirmClose(FileListEntry entry) {
103                 File f = expandFilename(directory, entry.getLink());
104                 if (f.isDirectory()) {
105                     JOptionPane.showMessageDialog(frame,
106                             Globals.lang("Target file cannot be a directory."), Globals.lang("Download file"),
107                             JOptionPane.ERROR_MESSAGE);
108                     return false;
109                 }
110                 if (f.exists()) {
111                     return JOptionPane.showConfirmDialog
112                         (frame, "'"+f.getName()+"' "+Globals.lang("exists. Overwrite file?"),
113                         Globals.lang("Download file"), JOptionPane.OK_CANCEL_OPTION)
114                             == JOptionPane.OK_OPTION;
115                 } else
116                     return true;
117             }
118         });
119         editor.setVisible(true);
120         // Editor closed. Go on:
121         if (editor.okPressed()) {
122             String dirPrefix = directory+System.getProperty("file.separator");
123             File toFile = expandFilename(directory, entry.getLink());
124             try {
125                 boolean success = Util.copyFile(tmp, toFile, true);
126                 if (!success) {
127                     // OOps, the file exists!
128                     System.out.println("File already exists! DownloadExternalFile.download()");
129                 }
130
131                 // If the local file is in or below the main file directory, change the
132                 // path to relative:
133                 if (entry.getLink().startsWith(directory) &&
134                         (entry.getLink().length() > dirPrefix.length())) {
135                     entry.setLink(entry.getLink().substring(dirPrefix.length()));
136                 }
137
138                 callback.downloadComplete(entry);
139             } catch (IOException ex) {
140                 ex.printStackTrace();
141             }
142
143             tmp.delete();
144         }
145         else {
146             // Cancelled. Just delete the temp file:
147             if (downloadFinished)
148                 tmp.delete();
149         }
150
151     }
152
153     /**
154      * Construct a File object pointing to the file linked, whether the link is
155      * absolute or relative to the main directory.
156      * @param directory The main directory.
157      * @param link The absolute or relative link.
158      * @return The expanded File.
159      */
160     private File expandFilename(String directory, String link) {
161         File toFile = new File(link);
162         // If this is a relative link, we should perhaps append the directory:
163         String dirPrefix = directory+System.getProperty("file.separator");
164         if (!toFile.isAbsolute()) {
165             toFile = new File(dirPrefix+link);
166         }
167         return toFile;
168     }
169
170     /**
171      * This is called by the download thread when download is completed.
172      */
173     public void downloadFinished() {
174         downloadFinished = true;
175         editor.getProgressBar().setVisible(false);
176         editor.getProgressBarLabel().setVisible(false);
177         editor.setOkEnabled(true);
178         editor.getProgressBar().setValue(editor.getProgressBar().getMaximum());
179     }
180
181     public String getSuggestedFileName(String res, String suffix) {
182         if (suffix == null) {
183             System.out.println("Link has no obvious extension (DownloadExternalFile.download()");
184         }
185
186         String plannedName = bibtexKey + "." + suffix;
187
188         /*
189         * [ 1548875 ] download pdf produces unsupported filename
190         *
191         * http://sourceforge.net/tracker/index.php?func=detail&aid=1548875&group_id=92314&atid=600306
192         *
193         */
194         if (Globals.ON_WIN) {
195             plannedName = plannedName.replaceAll(
196                     "\\?|\\*|\\<|\\>|\\||\\\"|\\:|\\.$|\\[|\\]", "");
197         } else if (Globals.ON_MAC) {
198             plannedName = plannedName.replaceAll(":", "");
199         }
200
201         return plannedName;
202     }
203
204     /**
205      * Look for the last '.' in the link, and returnthe following characters.
206      * This gives the extension for most reasonably named links.
207      *
208      * @param link The link
209      * @return The suffix, excluding the dot (e.g. ".pdf")
210      */
211     public String getSuffix(String link) {
212         int index = link.lastIndexOf('.');
213         if ((index <= 0) || (index == link.length() - 1)) // No occurence, or at the end
214             return null;
215         return link.substring(index + 1);
216     }
217
218     public String getFileDirectory(String link) {
219         // TODO: getFileDirectory()
220         return metaData.getFileDirectory(GUIGlobals.FILE_FIELD);
221     }
222
223     /**
224      * Callback interface that users of this class must implement in order to receive
225      * notification when download is complete.
226      */
227     public interface DownloadCallback {
228         public void downloadComplete(FileListEntry file);
229     }
230 }