package net.sf.jabref.external; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.JDialog; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; import net.sf.jabref.BaseAction; import net.sf.jabref.BasePanel; import net.sf.jabref.BibtexEntry; import net.sf.jabref.GUIGlobals; import net.sf.jabref.MetaData; import net.sf.jabref.Util; import net.sf.jabref.gui.FileListEntry; import net.sf.jabref.gui.FileListTableModel; /** * This class handles the task of looking up all external files linked for a set * of entries. This is useful for tasks where the user wants e.g. to send a database * with external files included. */ public class AccessLinksForEntries { /** * Look up all external files linked from (at least) one of the entries in a set. * This method does not verify the links. * * @param entries The set of entries. * @return A list of FileListEntry objects pointing to the external files. */ public static List<FileListEntry> getExternalLinksForEntries(List<BibtexEntry> entries) { List<FileListEntry> files = new ArrayList<FileListEntry>(); FileListTableModel model = new FileListTableModel(); for (Iterator<BibtexEntry> iterator = entries.iterator(); iterator.hasNext();) { BibtexEntry entry = iterator.next(); String links = entry.getField(GUIGlobals.FILE_FIELD); if (links == null) continue; model.setContent(links); for (int i=0; i<model.getRowCount(); i++) files.add(model.getEntry(i)); } return files; } /** * Take a list of external links and copy the referred files to a given directory. * This method should be run off the Event Dispatch Thread. A progress bar, if given, * will be updated on the EDT. * * @param files The list of file links. * @param toDir The directory to copy the files to. * @param metaData The MetaData for the database containing the external links. This is needed * because the database might have its own file directory. * @param prog A JProgressBar which will be updated to show the progress of the process. * This argument can be null if no progress bar is needed. * @param deleteOriginalFiles if true, the files in their original locations will be deleted * after copying, for each file whose source directory is different from the destination * directory differs. * @param callback An ActionListener which should be notified when the process is finished. * This parameter can be null if no callback is needed. */ public static void copyExternalLinksToDirectory(final List<FileListEntry> files, File toDir, MetaData metaData, final JProgressBar prog, boolean deleteOriginalFiles, final ActionListener callback) { if (prog != null) SwingUtilities.invokeLater(new Runnable() { public void run() { prog.setMaximum(files.size()); prog.setValue(0); prog.setIndeterminate(false); } }); Set<String> fileNames = new HashSet<String>(); int i=0; for (Iterator<FileListEntry> iterator = files.iterator(); iterator.hasNext();) { FileListEntry entry = iterator.next(); File file = new File(entry.getLink()); // We try to check the extension for the file: String name = file.getName(); int pos = name.lastIndexOf('.'); String extension = ((pos >= 0) && (pos < name.length() - 1)) ? name.substring(pos + 1) .trim().toLowerCase() : null; // Find the default directory for this field type, if any: String dir = metaData.getFileDirectory(extension); // Include the standard "file" directory: String fileDir = metaData.getFileDirectory(GUIGlobals.FILE_FIELD); // Include the directory of the bib file: String databaseDir = metaData.getFile().getParent(); File tmp = Util.expandFilename(entry.getLink(), new String[] { dir, fileDir, databaseDir }); if (tmp != null) file = tmp; // Check if we have arrived at an existing file: if (file.exists()) { if (fileNames.contains(name)) { // Oops, a file of that name already exists.... } else { fileNames.add(name); File destination = new File(toDir, name); // Check if the source and destination locations differ: if (!destination.equals(file)) { try { // Copy the file: Util.copyFile(file, destination, false); // Delete the original file if requested: if (deleteOriginalFiles) file.delete(); } catch (IOException ex) { ex.printStackTrace(); } } else { // Destination and source is the same. Do nothing. } // Update progress bar: i++; final int j = i; if (prog != null) SwingUtilities.invokeLater(new Runnable() { public void run() { prog.setValue(j); } }); } } else { // The link could not be resolved to an existing file. } } if (callback != null) { callback.actionPerformed(null); } } public static class SaveWithLinkedFiles extends BaseAction { private BasePanel panel; public SaveWithLinkedFiles(BasePanel panel) { this.panel = panel; } @Override public void action() throws Throwable { ArrayList<BibtexEntry> entries = new ArrayList<BibtexEntry>(); BibtexEntry[] sel = panel.getSelectedEntries(); for (int i = 0; i < sel.length; i++) { BibtexEntry bibtexEntry = sel[i]; entries.add(bibtexEntry); } final List<FileListEntry> links = AccessLinksForEntries.getExternalLinksForEntries(entries); for (Iterator<FileListEntry> iterator = links.iterator(); iterator.hasNext();) { FileListEntry entry = iterator.next(); System.out.println("Link: " + entry.getLink()); } final JProgressBar prog = new JProgressBar(); prog.setIndeterminate(true); final JDialog diag = new JDialog(panel.frame().getFrame(), false); diag.getContentPane().add(prog, BorderLayout.CENTER); diag.pack(); diag.setLocationRelativeTo(panel.frame()); diag.setVisible(true); Thread t = new Thread(new Runnable() { public void run() { AccessLinksForEntries.copyExternalLinksToDirectory(links, new File("/home/alver/tmp"), panel.metaData(), prog, false, new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { diag.dispose(); } }); } }); t.start(); } } }