net.sf.jabref.external.ExternalFilePanel.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jabref.external.ExternalFilePanel.java

Source

/*  Copyright (C) 2003-2011 JabRef contributors.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package net.sf.jabref.external;

import java.awt.Component;
import java.awt.GridLayout;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.xml.transform.TransformerException;

import net.sf.jabref.gui.JabRefFrame;
import net.sf.jabref.gui.OpenFileFilter;
import net.sf.jabref.gui.UrlDragDrop;
import net.sf.jabref.gui.entryeditor.EntryEditor;
import net.sf.jabref.gui.fieldeditors.FieldEditor;
import net.sf.jabref.gui.net.MonitoredURLDownload;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.logic.util.OS;
import net.sf.jabref.model.database.BibDatabase;
import net.sf.jabref.model.entry.BibEntry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jabref.*;
import net.sf.jabref.gui.FileDialogs;
import net.sf.jabref.logic.util.io.FileUtil;
import net.sf.jabref.logic.util.io.FileFinder;
import net.sf.jabref.logic.xmp.XMPUtil;

/**
 * Initial Version:
 *
 * @author alver
 * @version Date: May 7, 2005 Time: 7:17:42 PM
 *
 */
public class ExternalFilePanel extends JPanel {

    private final EntryEditor entryEditor;

    private final JabRefFrame frame;

    private final OpenFileFilter off;

    private BibEntry entry;

    private BibDatabase database;

    private final MetaData metaData;

    private static final Log LOGGER = LogFactory.getLog(ExternalFilePanel.class);

    public ExternalFilePanel(final String fieldName, final MetaData metaData, final BibEntry entry,
            final FieldEditor editor, final OpenFileFilter off) {
        this(null, metaData, null, fieldName, off, editor);
        this.entry = entry;
    }

    public ExternalFilePanel(final JabRefFrame frame, final MetaData metaData, final EntryEditor entryEditor,
            final String fieldName, final OpenFileFilter off, final FieldEditor editor) {

        this.frame = frame;
        this.metaData = metaData;
        this.off = off;
        this.entryEditor = entryEditor;

        setLayout(new GridLayout(2, 2));

        JButton browseBut = new JButton(Localization.lang("Browse"));
        JButton download = new JButton(Localization.lang("Download"));
        JButton auto = new JButton(Localization.lang("Auto"));
        JButton xmp = new JButton(Localization.lang("Write XMP"));
        xmp.setToolTipText(Localization.lang("Write BibtexEntry as XMP-metadata to PDF."));

        browseBut.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                browseFile(fieldName, editor);
                // editor.setText(chosenValue);
                entryEditor.storeFieldAction.actionPerformed(new ActionEvent(editor, 0, ""));
            }
        });

        download.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                downLoadFile(fieldName, editor, frame);
            }
        });

        auto.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JabRefExecutorService.INSTANCE.execute(autoSetFile(fieldName, editor));
            }
        });
        xmp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                pushXMP(fieldName, editor);
            }
        });

        add(browseBut);
        add(download);
        add(auto);
        add(xmp);

        // Add drag and drop support to the field
        if (editor != null) {
            ((JComponent) editor).setDropTarget(new DropTarget((Component) editor, DnDConstants.ACTION_NONE,
                    new UrlDragDrop(entryEditor, frame, editor)));
        }
    }

    /**
     * Change which entry this panel is operating on. This is used only when
     * this panel is not attached to an entry editor.
     */
    public void setEntry(BibEntry entry, BibDatabase database) {
        this.entry = entry;
        this.database = database;
    }

    private BibDatabase getDatabase() {
        return database != null ? database : entryEditor.getDatabase();
    }

    private BibEntry getEntry() {
        return entry != null ? entry : entryEditor.getEntry();
    }

    private Object getKey() {
        return getEntry().getCiteKey();
    }

    private void output(String s) {
        if (frame != null) {
            frame.output(s);
        }
    }

    private void pushXMP(final String fieldName, final FieldEditor editor) {

        JabRefExecutorService.INSTANCE.execute(new Runnable() {

            @Override
            public void run() {

                output(Localization.lang("Looking for pdf..."));

                // Find the default directory for this field type, if any:
                String[] dirs = metaData.getFileDirectory(fieldName);
                File file = null;
                if (dirs.length > 0) {
                    File tmp = FileUtil.expandFilename(editor.getText(), dirs);
                    if (tmp != null) {
                        file = tmp;
                    }
                }

                if (file == null) {
                    file = new File(editor.getText());
                }

                final File finalFile = file;

                output(Localization.lang("Writing XMP to '%0'...", finalFile.getName()));
                try {
                    XMPUtil.writeXMP(finalFile, getEntry(), getDatabase());
                    output(Localization.lang("Wrote XMP to '%0'.", finalFile.getName()));
                } catch (IOException e) {
                    JOptionPane.showMessageDialog(editor.getParent(),
                            Localization.lang("Error writing XMP to file: %0", e.getLocalizedMessage()),
                            Localization.lang("Writing XMP"), JOptionPane.ERROR_MESSAGE);
                    // String above and below
                    LOGGER.info("Error writing XMP to file: " + finalFile.getAbsolutePath(), e);
                    output(Localization.lang("Error writing XMP to file: %0", finalFile.getName()));

                } catch (TransformerException e) {
                    JOptionPane.showMessageDialog(editor.getParent(),
                            Localization.lang("Error converting BibTeX to XMP: %0", e.getLocalizedMessage()),
                            Localization.lang("Writing XMP"), JOptionPane.ERROR_MESSAGE);
                    LOGGER.info("Error while converting BibEntry to XMP " + finalFile.getAbsolutePath(), e);
                    output(Localization.lang("Error converting XMP to '%0'...", finalFile.getName()));
                }
            }
        });
    }

    public void browseFile(final String fieldName, final FieldEditor editor) {

        String[] dirs = metaData.getFileDirectory(fieldName);
        String directory = null;
        if (dirs.length > 0) {
            directory = dirs[0]; // Default to the first directory in the list
        }

        String dir = editor.getText();
        String retVal;

        if ((directory == null) || !new File(dir).isAbsolute()) {
            if (directory != null) {
                dir = directory;
            } else {
                dir = Globals.prefs.get(fieldName + Globals.FILETYPE_PREFS_EXT, "");
            }
        }

        String chosenFile = FileDialogs.getNewFile(frame, new File(dir), '.' + fieldName, JFileChooser.OPEN_DIALOG,
                false);

        if (chosenFile != null) {
            File newFile = new File(chosenFile);
            String position = newFile.getParent();

            if ((directory != null) && position.startsWith(directory)) {
                // Construct path relative to pdf base dir
                String relPath = position.substring(directory.length(), position.length()) + File.separator
                        + newFile.getName();

                // Remove leading path separator
                if (relPath.startsWith(File.separator)) {
                    relPath = relPath.substring(File.separator.length(), relPath.length());

                    // Set relative path as field value
                }

                retVal = relPath;
            } else {
                retVal = newFile.getPath();
            }

            editor.setText(retVal);
            Globals.prefs.put(fieldName + Globals.FILETYPE_PREFS_EXT, newFile.getPath());
        }
    }

    public void downLoadFile(final String fieldName, final FieldEditor fieldEditor, final Component parent) {

        final String res = JOptionPane.showInputDialog(parent, Localization.lang("Enter URL to download"));

        if ((res == null) || res.trim().isEmpty()) {
            return;
        }

        /*
         * If this panel belongs in an entry editor, note which entry is
         * currently shown:
         */
        final BibEntry targetEntry;
        if (entryEditor != null) {
            targetEntry = entryEditor.getEntry();
        } else {
            targetEntry = entry;
        }

        JabRefExecutorService.INSTANCE.execute(new Runnable() {

            public String getPlannedFileName(String result) {
                String suffix = off.getSuffix(result);
                if (suffix == null) {
                    suffix = '.' + fieldName.toLowerCase();
                }

                String plannedName;
                if (getKey() != null) {
                    plannedName = getKey() + suffix;
                } else {
                    plannedName = JOptionPane.showInputDialog(parent,
                            Localization.lang("BibTeX key not set. Enter a name for the downloaded file"));
                    if ((plannedName != null) && !off.accept(plannedName)) {
                        plannedName += suffix;
                    }
                }

                /*
                 * [ 1548875 ] download pdf produces unsupported filename
                 *
                 * http://sourceforge.net/tracker/index.php?func=detail&aid=1548875&group_id=92314&atid=600306
                 *
                 */
                if (OS.WINDOWS) {
                    plannedName = plannedName.replaceAll("\\?|\\*|\\<|\\>|\\||\\\"|\\:|\\.$|\\[|\\]", "");
                } else if (OS.OS_X) {
                    plannedName = plannedName.replaceAll(":", "");
                }

                return plannedName;
            }

            @Override
            public void run() {
                String originalText = fieldEditor.getText();
                fieldEditor.setEnabled(false);
                boolean updateEditor = true;

                try {
                    fieldEditor.setText(Localization.lang("Downloading..."));
                    output(Localization.lang("Downloading..."));
                    String plannedName = getPlannedFileName(res);

                    // Find the default directory for this field type:
                    String[] dirs = metaData.getFileDirectory(fieldName);
                    String directory = null;
                    // Look for the first one in the list that exists:
                    for (String dir : dirs) {
                        if (new File(dir).exists()) {
                            directory = dir;
                            break;
                        }
                    }
                    if (directory == null) {
                        if (dirs.length > 0) {
                            JOptionPane.showMessageDialog(parent,
                                    Localization.lang("Could not find directory for %0-files: %1", fieldName,
                                            dirs[0]),
                                    Localization.lang("Download file"), JOptionPane.ERROR_MESSAGE);
                        } else {
                            JOptionPane.showMessageDialog(parent,
                                    Localization.lang("No directory defined for %0-files", fieldName),
                                    Localization.lang("Download file"), JOptionPane.ERROR_MESSAGE);
                        }
                        return;
                    }
                    File file = new File(new File(directory), plannedName);

                    URL url = new URL(res);

                    try {
                        MonitoredURLDownload.buildMonitoredDownload(parent, url).downloadToFile(file);
                    } catch (IOException e2) {
                        JOptionPane.showMessageDialog(parent,
                                Localization.lang("Invalid URL") + ": " + e2.getMessage(),
                                Localization.lang("Download file"), JOptionPane.ERROR_MESSAGE);
                        LOGGER.info("Error while downloading " + url, e2);
                        return;
                    }
                    output(Localization.lang("Download completed"));

                    String textToSet = file.getPath();
                    if (textToSet.startsWith(directory)) {
                        // Construct path relative to pdf base dir
                        textToSet = textToSet.substring(directory.length(), textToSet.length());

                        // Remove leading path separator
                        if (textToSet.startsWith(File.separator)) {
                            textToSet = textToSet.substring(File.separator.length());
                        }
                    }

                    /*
                     * Check if we should update the editor text field, or
                     * update the target entry directly:
                     */
                    if ((entryEditor == null) || (entryEditor.getEntry() != targetEntry)) {
                        /*
                         * Editor has probably changed to show a different
                         * entry. So we must update the target entry directly
                         * and not set the text of the editor.
                         */
                        targetEntry.setField(fieldName, textToSet);
                        fieldEditor.setText(textToSet);
                        fieldEditor.setEnabled(true);
                        updateEditor = false;
                    } else {
                        /*
                         * Need to set the fieldEditor first before running
                         * updateField-Action, because otherwise we might get a
                         * race condition.
                         *
                         * (Hopefully a) Fix for: [ 1545601 ] downloading pdf
                         * corrupts pdf field text
                         *
                         * http://sourceforge.net/tracker/index.php?func=detail&aid=1545601&group_id=92314&atid=600306
                         */
                        fieldEditor.setText(textToSet);
                        fieldEditor.setEnabled(true);
                        updateEditor = false;
                        SwingUtilities.invokeLater(new Runnable() {

                            @Override
                            public void run() {
                                entryEditor.updateField(fieldEditor);
                            }
                        });
                    }

                } catch (MalformedURLException e1) {
                    JOptionPane.showMessageDialog(parent, Localization.lang("Invalid URL"),
                            Localization.lang("Download file"), JOptionPane.ERROR_MESSAGE);
                } finally {
                    // If stuff goes wrong along the road, put back original
                    // value
                    if (updateEditor) {
                        fieldEditor.setText(originalText);
                        fieldEditor.setEnabled(true);
                    }
                }
            }
        });
    }

    /**
     * Creates a Runnable that searches the external file directory for the given
     * field name, including subdirectories, and looks for files named after the
     * current entry's bibtex key.
     *
     * @param fieldName
     *            The field to set.
     * @param editor
     *            An EntryEditor instance where to set the value found.
     * @return A reference to the Runnable that can perform the operation.
     */
    public Runnable autoSetFile(final String fieldName, final FieldEditor editor) {
        Object o = getKey();
        if ((o == null) || (Globals.prefs.get(fieldName + Globals.DIR_SUFFIX) == null)) {
            output(Localization.lang("You must set both BibTeX key and %0 directory", fieldName.toUpperCase())
                    + '.');
            return null;
        }
        output(Localization.lang("Searching for %0 file", fieldName.toUpperCase()) + " '" + o + '.' + fieldName
                + "'...");

        return new Runnable() {

            @Override
            public void run() {
                /*
                 * Find the following directories to look in for:
                 *
                 * default directory for this field type.
                 *
                 * directory of bibtex-file. // NOT POSSIBLE at the moment.
                 *
                 * JabRef-directory.
                 */
                LinkedList<String> list = new LinkedList<>();
                String[] dirs = metaData.getFileDirectory(fieldName);
                Collections.addAll(list, dirs);

                String found = FileFinder.findPdf(getEntry(), fieldName, list.toArray(new String[list.size()]));// , off);

                // To activate findFile:
                // String found = Util.findFile(getEntry(), null, dir,
                // ".*[bibtexkey].*");

                if (found != null) {
                    editor.setText(found);
                    if (entryEditor != null) {
                        entryEditor.updateField(editor);
                    }
                    output(Localization.lang("%0 field set", fieldName.toUpperCase()) + '.');
                } else {
                    output(Localization.lang("No %0 found", fieldName.toUpperCase()) + '.');
                }

            }
        };
    }
}