Coverage Report - net.sf.jabref.external.DroppedFileHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
DroppedFileHandler
0%
0/176
0%
0/86
8.111
DroppedFileHandler$1
0%
0/5
0%
0/10
8.111
 
 1  
 package net.sf.jabref.external;
 2  
 
 3  
 import java.io.File;
 4  
 import java.io.IOException;
 5  
 import java.util.Iterator;
 6  
 import java.util.List;
 7  
 
 8  
 import javax.swing.*;
 9  
 import javax.swing.event.ChangeEvent;
 10  
 import javax.swing.event.ChangeListener;
 11  
 
 12  
 import net.sf.jabref.*;
 13  
 import net.sf.jabref.gui.MainTable;
 14  
 import net.sf.jabref.gui.FileListTableModel;
 15  
 import net.sf.jabref.gui.FileListEntry;
 16  
 import net.sf.jabref.undo.NamedCompound;
 17  
 import net.sf.jabref.undo.UndoableFieldChange;
 18  
 import net.sf.jabref.undo.UndoableInsertEntry;
 19  
 import net.sf.jabref.util.XMPUtil;
 20  
 
 21  
 import com.jgoodies.forms.builder.DefaultFormBuilder;
 22  
 import com.jgoodies.forms.debug.FormDebugPanel;
 23  
 import com.jgoodies.forms.layout.CellConstraints;
 24  
 import com.jgoodies.forms.layout.FormLayout;
 25  
 
 26  
 /**
 27  
  * This class holds the functionality of autolinking to a file that's dropped
 28  
  * onto an entry.
 29  
  * 
 30  
  * Options for handling the files are:
 31  
  * 
 32  
  * 1) Link to the file in its current position (disabled if the file is remote)
 33  
  * 
 34  
  * 2) Copy the file to ??? directory, rename after bibtex key, and extension
 35  
  * 
 36  
  * 3) Move the file to ??? directory, rename after bibtex key, and extension
 37  
  */
 38  0
 public class DroppedFileHandler {
 39  
     private JabRefFrame frame;
 40  
 
 41  
     private BasePanel panel;
 42  
 
 43  0
     private JRadioButton linkInPlace = new JRadioButton(), copyRadioButton = new JRadioButton(),
 44  
         moveRadioButton = new JRadioButton();
 45  
     
 46  0
     private JLabel destDirLabel = new JLabel();
 47  
 
 48  0
     private JCheckBox renameCheckBox = new JCheckBox();
 49  
 
 50  0
     private JTextField renameToTextBox = new JTextField(25);
 51  
     
 52  0
     private JPanel optionsPanel = new JPanel();
 53  
 
 54  0
     public DroppedFileHandler(JabRefFrame frame, BasePanel panel) {
 55  
 
 56  0
         this.frame = frame;
 57  0
         this.panel = panel;
 58  
 
 59  0
         ButtonGroup grp = new ButtonGroup();
 60  0
         grp.add(linkInPlace);
 61  0
         grp.add(copyRadioButton);
 62  0
         grp.add(moveRadioButton);
 63  0
         copyRadioButton.setSelected(true);
 64  
 
 65  0
         FormLayout layout = new FormLayout("left:15dlu,pref,pref,pref","bottom:14pt,pref,pref,pref,pref");
 66  0
         layout.setRowGroups(new int[][]{{1, 2, 3, 4, 5}});
 67  0
         DefaultFormBuilder builder = new DefaultFormBuilder(layout,        optionsPanel);
 68  0
         builder.setDefaultDialogBorder();
 69  0
         CellConstraints cc = new CellConstraints();
 70  
         
 71  0
         builder.add(linkInPlace, cc.xyw(1, 1, 4));
 72  0
         builder.add(destDirLabel, cc.xyw(1, 2, 4));
 73  0
         builder.add(copyRadioButton, cc.xyw(2, 3, 3));        
 74  0
         builder.add(moveRadioButton, cc.xyw(2, 4, 3));
 75  0
         builder.add(renameCheckBox, cc.xyw(2, 5, 1));
 76  0
         builder.add(renameToTextBox, cc.xyw(4, 5, 1));
 77  
         
 78  0
     }
 79  
 
 80  
     /**
 81  
      * Offer copy/move/linking options for a dragged external file. Perform the
 82  
      * chosen operation, if any.
 83  
      * 
 84  
      * @param fileName
 85  
      *            The name of the dragged file.
 86  
      * @param fileType
 87  
      *            The FileType associated with the file.
 88  
      * @param localFile
 89  
      *            Indicate whether this is a local file, or a remote file copied
 90  
      *            to a local temporary file.
 91  
      * @param mainTable
 92  
      *            The MainTable the file was dragged to.
 93  
      * @param dropRow
 94  
      *            The row where the file was dropped.
 95  
      */
 96  
     public void handleDroppedfile(String fileName, ExternalFileType fileType, boolean localFile,
 97  
         MainTable mainTable, int dropRow) {
 98  
 
 99  0
         BibtexEntry entry = mainTable.getEntryAt(dropRow);
 100  0
         handleDroppedfile(fileName, fileType, localFile, entry);
 101  0
     }
 102  
 
 103  
     /**
 104  
      *
 105  
      * @param fileName
 106  
      *        The name of the dragged file.
 107  
      * @param fileType
 108  
      *        The FileType associated with the file.
 109  
      * @param localFile
 110  
  *            Indicate whether this is a local file, or a remote file copied
 111  
  *            to a local temporary file.
 112  
      * @param entry
 113  
      *        The target entry for the drop.
 114  
      */
 115  
     public void handleDroppedfile(String fileName, ExternalFileType fileType, boolean localFile,
 116  
         BibtexEntry entry) {
 117  0
         NamedCompound edits = new NamedCompound(Globals.lang("Drop %0", fileType.extension));
 118  
 
 119  0
         if (tryXmpImport(fileName, fileType, localFile, edits)) {
 120  0
             edits.end();
 121  0
             panel.undoManager.addEdit(edits);
 122  0
             return;
 123  
         }
 124  
 
 125  
         // Show dialog
 126  0
         boolean newEntry = false;
 127  0
         String citeKey = entry.getCiteKey();
 128  0
         int reply = showLinkMoveCopyRenameDialog(fileName, fileType, citeKey, newEntry, false);
 129  
 
 130  0
         if (reply != JOptionPane.OK_OPTION)
 131  0
             return;
 132  
 
 133  
         /*
 134  
          * Ok, we're ready to go. See first if we need to do a file copy before
 135  
          * linking:
 136  
          */
 137  
         
 138  0
         boolean success = true;
 139  
         String destFilename;
 140  
 
 141  0
         if (linkInPlace.isSelected()) {
 142  0
             destFilename = fileName;
 143  
         } else {
 144  0
             destFilename = (renameCheckBox.isSelected() ? renameToTextBox.getText() : new File(fileName).getName());
 145  0
             if (copyRadioButton.isSelected()) {
 146  0
                 success = doCopy(fileName, fileType, destFilename, edits);
 147  0
             } else if (moveRadioButton.isSelected()) {
 148  0
                 success = doMove(fileName, fileType, destFilename, edits);
 149  
             }
 150  
         }
 151  
 
 152  0
         if (success) {
 153  0
             doLink(entry, fileType, destFilename, false, edits);
 154  0
             panel.markBaseChanged();
 155  0
             panel.updateEntryEditorIfShowing();
 156  
         }
 157  0
         edits.end();
 158  0
         panel.undoManager.addEdit(edits);
 159  
 
 160  0
     }
 161  
 
 162  
     private boolean tryXmpImport(String fileName, ExternalFileType fileType, boolean localFile,
 163  
         NamedCompound edits) {
 164  
 
 165  0
         if (!fileType.extension.equals("pdf")) {
 166  0
             return false;
 167  
         }
 168  
 
 169  0
         List<BibtexEntry> xmpEntriesInFile = null;
 170  
         try {
 171  0
             xmpEntriesInFile = XMPUtil.readXMP(fileName);
 172  0
         } catch (Exception e) {
 173  0
             return false;
 174  0
         }
 175  
 
 176  0
         if ((xmpEntriesInFile == null) || (xmpEntriesInFile.size() == 0)) {
 177  0
             return false;
 178  
         }
 179  
 
 180  0
         JLabel confirmationMessage = new JLabel(
 181  
             Globals
 182  
                 .lang("The PDF contains one or several bibtex-records.\nDo you want to import these as new entries into the current database?"));
 183  
 
 184  0
         int reply = JOptionPane.showConfirmDialog(frame, confirmationMessage, Globals.lang(
 185  
             "XMP metadata found in PDF: %0", fileName), JOptionPane.YES_NO_CANCEL_OPTION,
 186  
             JOptionPane.QUESTION_MESSAGE);
 187  
 
 188  0
         if (reply == JOptionPane.CANCEL_OPTION) {
 189  0
             return true; // The user canceled thus that we are done.
 190  
         }
 191  0
         if (reply == JOptionPane.NO_OPTION) {
 192  0
             return false;
 193  
         }
 194  
 
 195  
         // reply == JOptionPane.YES_OPTION)
 196  
 
 197  
         /*
 198  
          * TODO Extract Import functionality from ImportMenuItem then we could
 199  
          * do:
 200  
          * 
 201  
          * ImportMenuItem importer = new ImportMenuItem(frame, (mainTable ==
 202  
          * null), new PdfXmpImporter());
 203  
          * 
 204  
          * importer.automatedImport(new String[] { fileName });
 205  
          */
 206  
 
 207  0
         boolean isSingle = xmpEntriesInFile.size() == 1;
 208  0
         BibtexEntry single = (isSingle ? xmpEntriesInFile.get(0) : null);
 209  
 
 210  
        
 211  0
         boolean success = true;
 212  
 
 213  
         String destFilename;
 214  
 
 215  0
         if (linkInPlace.isSelected()) {
 216  0
             destFilename = fileName;
 217  
         } else {
 218  0
             if (renameCheckBox.isSelected()) {
 219  0
                 destFilename = fileName;
 220  
             } else {
 221  0
                 destFilename = single.getCiteKey() + "." + fileType.extension;
 222  
             }
 223  
 
 224  0
             if (copyRadioButton.isSelected()) {
 225  0
                 success = doCopy(fileName, fileType, destFilename, edits);
 226  0
             } else if (moveRadioButton.isSelected()) {
 227  0
                 success = doMove(fileName, fileType, destFilename, edits);
 228  
             }
 229  
         }
 230  0
         if (success) {
 231  
 
 232  0
             Iterator<BibtexEntry> it = xmpEntriesInFile.iterator();
 233  
 
 234  0
             while (it.hasNext()) {
 235  
                 try {
 236  0
                     BibtexEntry entry = it.next();
 237  0
                     entry.setId(Util.createNeutralId());
 238  0
                     edits.addEdit(new UndoableInsertEntry(panel.getDatabase(), entry, panel));
 239  0
                     panel.getDatabase().insertEntry(entry);
 240  0
                     doLink(entry, fileType, destFilename, true, edits);
 241  0
                 } catch (KeyCollisionException ex) {
 242  
 
 243  0
                 }
 244  
             }
 245  0
             panel.markBaseChanged();
 246  0
             panel.updateEntryEditorIfShowing();
 247  
         }
 248  0
         return true;
 249  
     }
 250  
 
 251  
     public int showLinkMoveCopyRenameDialog(String linkFileName, ExternalFileType fileType,
 252  
         String citeKey, boolean newEntry, final boolean multipleEntries) {
 253  
             
 254  0
             String dialogTitle = Globals.lang("Link to file %0", linkFileName);
 255  
         //String dir = panel.metaData().getFileDirectory(fileType.getFieldName());
 256  0
         String dir = panel.metaData().getFileDirectory(GUIGlobals.FILE_FIELD);
 257  0
         if ((dir == null) || !(new File(dir)).exists()) {
 258  0
             destDirLabel.setText(Globals.lang("File directory is not set or does not exist."));
 259  0
             copyRadioButton.setEnabled(false);
 260  0
             moveRadioButton.setEnabled(false);
 261  0
             renameToTextBox.setEnabled(false);
 262  0
             renameCheckBox.setEnabled(false);
 263  0
             linkInPlace.setSelected(true);
 264  
         } else {
 265  0
             destDirLabel.setText(Globals.lang("File directory is '%0':", dir));
 266  0
             copyRadioButton.setEnabled(true);
 267  0
             moveRadioButton.setEnabled(true);
 268  0
             renameToTextBox.setEnabled(true);
 269  0
             renameCheckBox.setEnabled(true);
 270  
         }
 271  
         
 272  0
         ChangeListener cl = new ChangeListener() {
 273  
                         public void stateChanged(ChangeEvent arg0) {
 274  0
                                 renameCheckBox.setEnabled(!linkInPlace.isSelected()
 275  
                                                 &&  (!multipleEntries));
 276  0
                                 renameToTextBox.setEnabled(!linkInPlace.isSelected()
 277  
                                                 &&  (!multipleEntries));
 278  0
                                 if (multipleEntries) { renameToTextBox.setText("Multiple entries"); }
 279  0
                         }
 280  
                 };
 281  
 
 282  0
                 if (multipleEntries) {
 283  0
                         linkInPlace.setText(Globals
 284  
                                         .lang("Leave files in their current directory."));
 285  0
                         copyRadioButton.setText(Globals.lang("Copy files to file directory."));
 286  
 
 287  0
                         moveRadioButton.setText(Globals.lang("Move files to file directory."));
 288  
                 } else {
 289  0
                         linkInPlace.setText(Globals
 290  
                                         .lang("Leave file in its current directory."));
 291  0
                         copyRadioButton.setText(Globals.lang("Copy file to file directory."));
 292  0
                         moveRadioButton.setText(Globals.lang("Move file to file directory."));
 293  
                 }
 294  
                 
 295  0
         renameCheckBox.setText(Globals.lang("Rename file to") + ": ");
 296  0
         renameToTextBox.setText(citeKey == null ? "default" : citeKey + "." + fileType.extension);
 297  0
         linkInPlace.addChangeListener(cl);
 298  0
         cl.stateChanged(new ChangeEvent(linkInPlace));
 299  
 
 300  
         try {
 301  0
                 Object[] messages = {"How would you like to link to " + linkFileName + "?", optionsPanel}; 
 302  0
             return JOptionPane.showConfirmDialog(frame, messages, dialogTitle,
 303  
                 JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
 304  
         } finally {
 305  0
             linkInPlace.removeChangeListener(cl);
 306  
         }
 307  
     }
 308  
     
 309  
     /**
 310  
      * Make a extension to the file.
 311  
      * 
 312  
      * @param entry
 313  
      *            The entry to extension from.
 314  
      * @param fileType
 315  
      *            The FileType associated with the file.
 316  
      * @param filename
 317  
      *            The path to the file.
 318  
      * @param edits
 319  
      *            An NamedCompound action this action is to be added to. If none
 320  
      *            is given, the edit is added to the panel's undoManager.
 321  
      */
 322  
     private void doLink(BibtexEntry entry, ExternalFileType fileType, String filename,
 323  
         boolean avoidDuplicate, NamedCompound edits) {
 324  
 
 325  
 
 326  0
         String oldValue = entry.getField(GUIGlobals.FILE_FIELD);
 327  0
         FileListTableModel tm = new FileListTableModel();
 328  0
         if (oldValue != null)
 329  0
             tm.setContent(oldValue);
 330  
 
 331  
         // If avoidDuplicate==true, we should check if this file is already linked:
 332  0
         if (avoidDuplicate) {
 333  
             // For comparison, find the absolute filename:
 334  0
             String fileDir = panel.metaData().getFileDirectory(GUIGlobals.FILE_FIELD);
 335  0
             String absFilename = (!(new File(filename).isAbsolute()) && (fileDir != null)) ?
 336  
                     new File(fileDir, filename).getAbsolutePath() : filename;
 337  0
             System.out.println("absFilename: "+absFilename);
 338  
             
 339  0
             for (int i=0; i<tm.getRowCount(); i++) {
 340  0
                 FileListEntry flEntry = tm.getEntry(i);
 341  
                 // Find the absolute filename for this existing link:
 342  0
                 String absName = (!(new File(flEntry.getLink()).isAbsolute()) && (fileDir != null)) ?
 343  
                         new File(fileDir, flEntry.getLink()).getAbsolutePath() : flEntry.getLink();
 344  0
                 System.out.println("absName: "+absName);
 345  
                 // If the filenames are equal, we don't need to link, so we simply return:
 346  0
                 if (absFilename.equals(absName))
 347  0
                     return;
 348  
             }
 349  
         }
 350  
 
 351  0
         tm.addEntry(tm.getRowCount(), new FileListEntry("", filename, fileType));
 352  0
         String newValue = tm.getStringRepresentation();
 353  0
         UndoableFieldChange edit = new UndoableFieldChange(entry, GUIGlobals.FILE_FIELD,
 354  
                 oldValue, newValue);
 355  0
         entry.setField(GUIGlobals.FILE_FIELD, newValue);
 356  
 
 357  0
         if (edits == null) {
 358  0
             panel.undoManager.addEdit(edit);
 359  
         } else {
 360  0
             edits.addEdit(edit);
 361  
         }
 362  0
     }
 363  
 
 364  
     /**
 365  
      * Move the given file to the base directory for its file type, and rename
 366  
      * it to the given filename.
 367  
      * 
 368  
      * @param fileName
 369  
      *            The name of the source file.
 370  
      * @param fileType
 371  
      *            The FileType associated with the file.
 372  
      * @param destFilename
 373  
      *            The destination filename.
 374  
      * @param edits
 375  
      *            TODO we should be able to undo this action
 376  
      * @return true if the operation succeeded.
 377  
      */
 378  
     private boolean doMove(String fileName, ExternalFileType fileType, String destFilename,
 379  
         NamedCompound edits) {
 380  0
         String dir = panel.metaData().getFileDirectory(GUIGlobals.FILE_FIELD);
 381  0
         if ((dir == null) || !(new File(dir)).exists()) {
 382  
             // OOps, we don't know which directory to put it in, or the given
 383  
             // dir doesn't exist....
 384  
             // This should not happen!!
 385  0
             return false;
 386  
         }
 387  0
         File fromFile = new File(fileName);
 388  0
         File toFile = new File(dir + System.getProperty("file.separator") + destFilename);
 389  0
         if (toFile.exists()) {
 390  0
                 int answer = JOptionPane.showConfirmDialog(frame,
 391  
                                 toFile.getAbsolutePath() + " exists. Overwrite?", "Overwrite file?", 
 392  
                                 JOptionPane.YES_NO_OPTION);
 393  0
                 if (answer == JOptionPane.NO_OPTION) {
 394  0
                         return false;
 395  
                 }
 396  
         }
 397  
  
 398  0
         if (!fromFile.renameTo(toFile)) {
 399  0
                 JOptionPane.showMessageDialog(frame,
 400  
                                 "There was an error moving the file. Please move the file manually and link in place.",
 401  
                                 "Error moving file", JOptionPane.ERROR_MESSAGE);
 402  0
                 return false;
 403  
         } else {
 404  0
                 return true;
 405  
         }
 406  
 
 407  
     }
 408  
 
 409  
     /**
 410  
      * Copy the given file to the base directory for its file type, and give it
 411  
      * the given name.
 412  
      * 
 413  
      * @param fileName
 414  
      *            The name of the source file.
 415  
      * @param fileType
 416  
      *            The FileType associated with the file.
 417  
      * @param toFile
 418  
      *            The destination filename. An existing path-component will be removed.
 419  
      * @param edits
 420  
      *            TODO we should be able to undo this!
 421  
      * @return
 422  
      */
 423  
     private boolean doCopy(String fileName, ExternalFileType fileType, String toFile,
 424  
         NamedCompound edits) {
 425  
 
 426  0
         String dir = panel.metaData().getFileDirectory(GUIGlobals.FILE_FIELD);
 427  0
         if ((dir == null) || !(new File(dir)).exists()) {
 428  
             // OOps, we don't know which directory to put it in, or the given
 429  
             // dir doesn't exist....
 430  0
             System.out.println("dir: " + dir + "\t ext: " + fileType.getExtension());
 431  0
             return false;
 432  
         }
 433  0
         toFile = new File(toFile).getName();
 434  
         
 435  0
         File destFile = new File(new StringBuffer(dir).append(System.getProperty("file.separator"))
 436  
             .append(toFile).toString());
 437  0
         if (destFile.equals(new File(fileName))){
 438  
             // File is already in the correct position. Don't override!
 439  0
             return true;
 440  
         }
 441  
         
 442  0
         if (destFile.exists()) {
 443  0
             int answer = JOptionPane.showConfirmDialog(frame, "'" + destFile.getPath() + "' "
 444  
                 + Globals.lang("exists. Overwrite?"), Globals.lang("File exists"),
 445  
                 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
 446  0
             if (answer == JOptionPane.NO_OPTION)
 447  0
                 return false;
 448  
         }
 449  
         try {
 450  0
             Util.copyFile(new File(fileName), destFile, true);
 451  0
         } catch (IOException e) {
 452  0
             e.printStackTrace();
 453  0
             return false;
 454  0
         }
 455  
 
 456  0
         return true;
 457  
     }
 458  
 
 459  
 }