From 4c3e520a32f986e0df8a6b7187cf6b2dfe322ccc Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 11 Sep 2018 10:55:56 +0200 Subject: [PATCH 1/4] Fix that "Rename and move file" throws file not found exception (#4317) * Fix that "Rename and move file" throws file not found exception Fixes #4307. Moreover, I refactored a bit how files are renamed/moved to the default file directory. This should make it easier to reuse the code. * Fix tests --- .../org/jabref/cli/ArgumentProcessor.java | 2 +- src/main/java/org/jabref/gui/BasePanel.java | 6 +- .../java/org/jabref/gui/PreviewPanel.java | 7 +- .../org/jabref/gui/actions/CleanupAction.java | 8 +- .../gui/actions/IntegrityCheckAction.java | 2 +- .../jabref/gui/actions/WriteXMPAction.java | 2 +- .../gui/cleanup/CleanupPresetPanel.java | 2 +- .../jabref/gui/copyfiles/CopyFilesTask.java | 2 +- .../org/jabref/gui/desktop/JabRefDesktop.java | 4 +- .../DocumentViewerViewModel.java | 4 +- .../jabref/gui/entryeditor/EntryEditor.java | 7 +- .../jabref/gui/exporter/ExportCommand.java | 4 +- .../gui/exporter/ExportToClipboardAction.java | 2 +- .../externalfiles/AutoSetFileLinksUtil.java | 8 +- .../gui/externalfiles/AutoSetLinks.java | 2 +- .../externalfiles/DownloadExternalFile.java | 2 +- .../gui/externalfiles/DroppedFileHandler.java | 26 +-- .../ExternalFilesEntryLinker.java | 17 +- .../gui/externalfiles/FindFullTextAction.java | 2 +- .../externalfiles/NewDroppedFileHandler.java | 10 +- .../TransferableFileLinkSelection.java | 4 +- .../ExternalFileMenuItem.java | 2 +- .../jabref/gui/fieldeditors/FieldEditors.java | 2 +- .../gui/fieldeditors/LinkedFileViewModel.java | 118 ++++++------ .../LinkedFilesEditorViewModel.java | 12 +- .../jabref/gui/filelist/AttachFileAction.java | 2 +- .../gui/filelist/FileListEntryEditor.java | 8 +- .../LinkedFilesEditDialogViewModel.java | 6 +- .../gui/importer/EntryFromFileCreator.java | 4 +- .../gui/importer/UnlinkedPDFFileFilter.java | 2 +- .../org/jabref/gui/maintable/MainTable.java | 6 +- .../org/jabref/gui/preferences/FileTab.java | 6 +- .../jabref/gui/worker/SendAsEMailAction.java | 2 +- .../logic/cleanup/CleanupPreferences.java | 26 +-- .../jabref/logic/cleanup/CleanupWorker.java | 24 +-- .../logic/cleanup/MoveFilesCleanup.java | 109 ++--------- .../logic/cleanup/RelativePathsCleanup.java | 11 +- .../logic/cleanup/RenamePdfCleanup.java | 167 ++--------------- .../externalfiles/LinkedFileHandler.java | 144 +++++++++++++++ .../jabref/logic/integrity/FieldCheckers.java | 10 +- .../jabref/logic/integrity/FileChecker.java | 11 +- .../logic/integrity/IntegrityCheck.java | 10 +- .../logic/pdf/EntryAnnotationImporter.java | 8 +- .../jabref/logic/pdf/FileAnnotationCache.java | 6 +- .../logic/util/io/DatabaseFileLookup.java | 8 +- .../org/jabref/logic/util/io/FileUtil.java | 19 +- .../migrations/FileLinksUpgradeWarning.java | 16 +- .../model/database/BibDatabaseContext.java | 16 +- .../org/jabref/model/entry/LinkedFile.java | 23 ++- ...yPreferences.java => FilePreferences.java} | 21 ++- .../org/jabref/model/metadata/MetaData.java | 2 +- .../org/jabref/model/util/FileHelper.java | 8 +- .../org/jabref/pdfimport/PdfImporter.java | 6 +- .../jabref/preferences/JabRefPreferences.java | 26 +-- .../preferences/PreferencesService.java | 4 +- src/main/resources/l10n/JabRef_en.properties | 3 +- .../AutoSetFileLinksUtilTest.java | 6 +- .../fieldeditors/LinkedFileViewModelTest.java | 24 ++- .../logic/cleanup/CleanupWorkerTest.java | 18 +- .../jabref/logic/cleanup/ISSNCleanupTest.java | 5 +- .../logic/cleanup/MoveFilesCleanupTest.java | 174 ++++++------------ .../logic/cleanup/RenamePdfCleanupTest.java | 78 ++------ .../logic/integrity/IntegrityCheckTest.java | 8 +- .../pdf/EntryAnnotationImporterTest.java | 4 +- .../database/BibDatabaseContextTest.java | 8 +- 65 files changed, 558 insertions(+), 738 deletions(-) create mode 100644 src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java rename src/main/java/org/jabref/model/metadata/{FileDirectoryPreferences.java => FilePreferences.java} (66%) diff --git a/src/main/java/org/jabref/cli/ArgumentProcessor.java b/src/main/java/org/jabref/cli/ArgumentProcessor.java index 120fd25c775..9d2509b922a 100644 --- a/src/main/java/org/jabref/cli/ArgumentProcessor.java +++ b/src/main/java/org/jabref/cli/ArgumentProcessor.java @@ -454,7 +454,7 @@ private void exportFile(List loaded, String[] data) { BibDatabaseContext databaseContext = pr.getDatabaseContext(); databaseContext.setDatabaseFile(theFile); Globals.prefs.fileDirForDatabase = databaseContext - .getFileDirectories(Globals.prefs.getFileDirectoryPreferences()); + .getFileDirectories(Globals.prefs.getFilePreferences()); System.out.println(Localization.lang("Exporting") + ": " + data[0]); Optional exporter = Globals.exportFactory.getExporterByName(data[1]); if (!exporter.isPresent()) { diff --git a/src/main/java/org/jabref/gui/BasePanel.java b/src/main/java/org/jabref/gui/BasePanel.java index b6b506375e2..efe88110716 100644 --- a/src/main/java/org/jabref/gui/BasePanel.java +++ b/src/main/java/org/jabref/gui/BasePanel.java @@ -191,7 +191,7 @@ public BasePanel(JabRefFrame frame, BasePanelPreferences preferences, BibDatabas this.tableModel = new MainTableDataModel(getBibDatabaseContext()); citationStyleCache = new CitationStyleCache(bibDatabaseContext); - annotationCache = new FileAnnotationCache(bibDatabaseContext, Globals.prefs.getFileDirectoryPreferences()); + annotationCache = new FileAnnotationCache(bibDatabaseContext, Globals.prefs.getFilePreferences()); setupMainPanel(); @@ -368,7 +368,7 @@ private void setupActions() { actions.put(Actions.OPEN_EXTERNAL_FILE, this::openExternalFile); actions.put(Actions.OPEN_FOLDER, () -> JabRefExecutorService.INSTANCE.execute(() -> { - final List files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(), bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())); + final List files = FileUtil.getListOfLinkedFiles(mainTable.getSelectedEntries(), bibDatabaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())); for (final Path f : files) { try { JabRefDesktop.openFolderAndSelectFile(f.toAbsolutePath()); @@ -1398,7 +1398,7 @@ public void searchAndOpen() { } final Set types = ExternalFileTypes.getInstance().getExternalFileTypeSelection(); - final List dirs = basePanel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()); + final List dirs = basePanel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); final List extensions = types.stream().map(ExternalFileType::getExtension).collect(Collectors.toList()); // Run the search operation: diff --git a/src/main/java/org/jabref/gui/PreviewPanel.java b/src/main/java/org/jabref/gui/PreviewPanel.java index 7a0b16a16a7..cece4cdda37 100644 --- a/src/main/java/org/jabref/gui/PreviewPanel.java +++ b/src/main/java/org/jabref/gui/PreviewPanel.java @@ -41,7 +41,6 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.event.FieldChangedEvent; -import org.jabref.preferences.JabRefPreferences; import org.jabref.preferences.PreviewPreferences; import com.google.common.eventbus.Subscribe; @@ -89,12 +88,10 @@ public PreviewPanel(BasePanel panel, BibDatabaseContext databaseContext, KeyBind this.keyBindingRepository = keyBindingRepository; fileHandler = new NewDroppedFileHandler(dialogService, databaseContext, externalFileTypes, - Globals.prefs.getFileDirectoryPreferences(), - Globals.prefs.getCleanupPreferences(Globals.journalAbbreviationLoader).getFileDirPattern(), + Globals.prefs.getFilePreferences(), Globals.prefs.getImportFormatPreferences(), Globals.prefs.getUpdateFieldPreferences(), - Globals.getFileUpdateMonitor(), - Globals.prefs.get(JabRefPreferences.IMPORT_FILENAMEPATTERN)); + Globals.getFileUpdateMonitor()); // Set up scroll pane for preview pane setFitToHeight(true); diff --git a/src/main/java/org/jabref/gui/actions/CleanupAction.java b/src/main/java/org/jabref/gui/actions/CleanupAction.java index ef179b58b52..a4814735c8d 100644 --- a/src/main/java/org/jabref/gui/actions/CleanupAction.java +++ b/src/main/java/org/jabref/gui/actions/CleanupAction.java @@ -74,8 +74,6 @@ private void doCleanup(CleanupPreset preset, BibEntry entry, NamedCompound ce) { Globals.journalAbbreviationLoader)); List changes = cleaner.cleanup(preset, entry); - unsuccessfulRenames = cleaner.getUnsuccessfulRenames(); - if (changes.isEmpty()) { return; } @@ -90,11 +88,7 @@ private void showResults() { if (isCanceled) { return; } - if (unsuccessfulRenames > 0) { //Rename failed for at least one entry - dialogService.showErrorDialogAndWait( - Localization.lang("Autogenerate PDF Names"), - Localization.lang("File rename failed for %0 entries.", Integer.toString(unsuccessfulRenames))); - } + if (modifiedEntriesCount > 0) { panel.updateEntryEditorIfShowing(); panel.markBaseChanged(); diff --git a/src/main/java/org/jabref/gui/actions/IntegrityCheckAction.java b/src/main/java/org/jabref/gui/actions/IntegrityCheckAction.java index d5702800f5f..ad07a4dde80 100644 --- a/src/main/java/org/jabref/gui/actions/IntegrityCheckAction.java +++ b/src/main/java/org/jabref/gui/actions/IntegrityCheckAction.java @@ -48,7 +48,7 @@ public IntegrityCheckAction(JabRefFrame frame) { @Override public void execute() { IntegrityCheck check = new IntegrityCheck(frame.getCurrentBasePanel().getBibDatabaseContext(), - Globals.prefs.getFileDirectoryPreferences(), + Globals.prefs.getFilePreferences(), Globals.prefs.getBibtexKeyPatternPreferences(), Globals.journalAbbreviationLoader.getRepository(Globals.prefs.getJournalAbbreviationPreferences()), Globals.prefs.getBoolean(JabRefPreferences.ENFORCE_LEGAL_BIBTEX_KEY)); diff --git a/src/main/java/org/jabref/gui/actions/WriteXMPAction.java b/src/main/java/org/jabref/gui/actions/WriteXMPAction.java index e2d549e2c4e..9d77d998195 100644 --- a/src/main/java/org/jabref/gui/actions/WriteXMPAction.java +++ b/src/main/java/org/jabref/gui/actions/WriteXMPAction.java @@ -111,7 +111,7 @@ private void writeXMP() { // Make a list of all PDFs linked from this entry: List files = entry.getFiles().stream() .filter(file -> file.getFileType().equalsIgnoreCase("pdf")) - .map(file -> file.findIn(basePanel.getBibDatabaseContext(), Globals.prefs.getFileDirectoryPreferences())) + .map(file -> file.findIn(basePanel.getBibDatabaseContext(), Globals.prefs.getFilePreferences())) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java index 32c3dbeb61b..b5127707c6c 100644 --- a/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java +++ b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java @@ -53,7 +53,7 @@ private void init() { cleanUpISSN = new JCheckBox(Localization.lang("Reformat ISSN")); Optional firstExistingDir = databaseContext - .getFirstExistingFileDir(JabRefPreferences.getInstance().getFileDirectoryPreferences()); + .getFirstExistingFileDir(JabRefPreferences.getInstance().getFilePreferences()); if (firstExistingDir.isPresent()) { cleanUpMovePDF = new JCheckBox(Localization.lang("Move linked files to default file directory %0", firstExistingDir.get().toString())); diff --git a/src/main/java/org/jabref/gui/copyfiles/CopyFilesTask.java b/src/main/java/org/jabref/gui/copyfiles/CopyFilesTask.java index 7dd83dbd4be..aec95051171 100644 --- a/src/main/java/org/jabref/gui/copyfiles/CopyFilesTask.java +++ b/src/main/java/org/jabref/gui/copyfiles/CopyFilesTask.java @@ -74,7 +74,7 @@ protected List call() throws InterruptedException, LinkedFile fileName = files.get(j); - Optional fileToExport = fileName.findIn(databaseContext, Globals.prefs.getFileDirectoryPreferences()); + Optional fileToExport = fileName.findIn(databaseContext, Globals.prefs.getFilePreferences()); newPath = OptionalUtil.combine(Optional.of(exportPath), fileToExport, resolvePathFilename); diff --git a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java index c9c1a515aa4..aa7f9dd22fc 100644 --- a/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java +++ b/src/main/java/org/jabref/gui/desktop/JabRefDesktop.java @@ -57,7 +57,7 @@ public static void openExternalViewer(BibDatabaseContext databaseContext, String String fieldName = initialFieldName; if (FieldName.PS.equals(fieldName) || FieldName.PDF.equals(fieldName)) { // Find the default directory for this field type: - List dir = databaseContext.getFileDirectories(fieldName, Globals.prefs.getFileDirectoryPreferences()); + List dir = databaseContext.getFileDirectories(fieldName, Globals.prefs.getFilePreferences()); Optional file = FileHelper.expandFilename(link, dir); @@ -128,7 +128,7 @@ public static boolean openExternalFileAnyFormat(final BibDatabaseContext databas return true; } - Optional file = FileHelper.expandFilename(databaseContext, link, Globals.prefs.getFileDirectoryPreferences()); + Optional file = FileHelper.expandFilename(databaseContext, link, Globals.prefs.getFilePreferences()); if (file.isPresent() && Files.exists(file.get())) { // Open the file: String filePath = file.get().toString(); diff --git a/src/main/java/org/jabref/gui/documentviewer/DocumentViewerViewModel.java b/src/main/java/org/jabref/gui/documentviewer/DocumentViewerViewModel.java index 22e54a97d28..61ac47eb2ef 100644 --- a/src/main/java/org/jabref/gui/documentviewer/DocumentViewerViewModel.java +++ b/src/main/java/org/jabref/gui/documentviewer/DocumentViewerViewModel.java @@ -112,8 +112,8 @@ private void setCurrentDocument(Path path) { public void switchToFile(LinkedFile file) { if (file != null) { stateManager.getActiveDatabase().ifPresent(database -> - file.findIn(database, Globals.prefs.getFileDirectoryPreferences()) - .ifPresent(this::setCurrentDocument)); + file.findIn(database, Globals.prefs.getFilePreferences()) + .ifPresent(this::setCurrentDocument)); } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index b71d2b3e727..505aed2298a 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -48,7 +48,6 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.util.FileUpdateMonitor; -import org.jabref.preferences.JabRefPreferences; import com.airhacks.afterburner.views.ViewLoader; import org.fxmisc.easybind.EasyBind; @@ -102,12 +101,10 @@ public EntryEditor(BasePanel panel, EntryEditorPreferences preferences, FileUpda this.dialogService = dialogService; fileHandler = new NewDroppedFileHandler(dialogService, databaseContext, externalFileTypes, - Globals.prefs.getFileDirectoryPreferences(), - Globals.prefs.getCleanupPreferences(Globals.journalAbbreviationLoader).getFileDirPattern(), + Globals.prefs.getFilePreferences(), Globals.prefs.getImportFormatPreferences(), Globals.prefs.getUpdateFieldPreferences(), - Globals.getFileUpdateMonitor(), - Globals.prefs.get(JabRefPreferences.IMPORT_FILENAMEPATTERN)); + Globals.getFileUpdateMonitor()); ViewLoader.view(this) .root(this) diff --git a/src/main/java/org/jabref/gui/exporter/ExportCommand.java b/src/main/java/org/jabref/gui/exporter/ExportCommand.java index 86c70a8a526..102d36d9e24 100644 --- a/src/main/java/org/jabref/gui/exporter/ExportCommand.java +++ b/src/main/java/org/jabref/gui/exporter/ExportCommand.java @@ -86,8 +86,8 @@ private void export(Path file, FileChooser.ExtensionFilter selectedExtensionFilt // so formatters can resolve linked files correctly. // (This is an ugly hack!) Globals.prefs.fileDirForDatabase = frame.getCurrentBasePanel() - .getBibDatabaseContext() - .getFileDirectories(Globals.prefs.getFileDirectoryPreferences()); + .getBibDatabaseContext() + .getFileDirectories(Globals.prefs.getFilePreferences()); // Make sure we remember which filter was used, to set // the default for next time: diff --git a/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java b/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java index ee1cfad868d..626fb96723a 100644 --- a/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java +++ b/src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java @@ -73,7 +73,7 @@ private String exportToClipboard(Exporter exporter) { // Set the global variable for this database's file directory before exporting, // so formatters can resolve linked files correctly. // (This is an ugly hack!) - Globals.prefs.fileDirForDatabase = panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()).stream().map(Path::toString).collect(Collectors.toList()); + Globals.prefs.fileDirForDatabase = panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()).stream().map(Path::toString).collect(Collectors.toList()); Path tmp = null; try { diff --git a/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java b/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java index 1ad53ec5559..f1f07088a16 100644 --- a/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java +++ b/src/main/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtil.java @@ -18,7 +18,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.util.FileHelper; import org.slf4j.Logger; @@ -31,8 +31,8 @@ public class AutoSetFileLinksUtil { private AutoLinkPreferences autoLinkPreferences; private ExternalFileTypes externalFileTypes; - public AutoSetFileLinksUtil(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) { - this(databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences), autoLinkPreferences, externalFileTypes); + public AutoSetFileLinksUtil(BibDatabaseContext databaseContext, FilePreferences filePreferences, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) { + this(databaseContext.getFileDirectoriesAsPaths(filePreferences), autoLinkPreferences, externalFileTypes); } public AutoSetFileLinksUtil(List directories, AutoLinkPreferences autoLinkPreferences, ExternalFileTypes externalFileTypes) { @@ -68,7 +68,7 @@ public List findAssociatedNotLinkedFiles(BibEntry entry) throws IOEx .orElse(Optional.of(new UnknownExternalFileType(""))); String strType = type.isPresent() ? type.get().getName() : ""; - String relativeFilePath = FileUtil.shortenFileName(foundFile, directories).toString(); + String relativeFilePath = FileUtil.relativize(foundFile, directories).toString(); LinkedFile linkedFile = new LinkedFile("", relativeFilePath, strType); linkedFiles.add(linkedFile); } diff --git a/src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java b/src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java index 1e9d9813dee..d874493a98d 100644 --- a/src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java +++ b/src/main/java/org/jabref/gui/externalfiles/AutoSetLinks.java @@ -89,7 +89,7 @@ public static Runnable autoSetLinks(final List entries, final NamedCom Runnable r = () -> { boolean foundAny = false; - AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(databaseContext, Globals.prefs.getFileDirectoryPreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance()); + AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(databaseContext, Globals.prefs.getFilePreferences(), Globals.prefs.getAutoLinkPreferences(), ExternalFileTypes.getInstance()); for (BibEntry entry : entries) { diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadExternalFile.java b/src/main/java/org/jabref/gui/externalfiles/DownloadExternalFile.java index e70395197e6..b4e1ad794db 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadExternalFile.java +++ b/src/main/java/org/jabref/gui/externalfiles/DownloadExternalFile.java @@ -202,7 +202,7 @@ public void download(URL url, String mimeType, final DownloadCallback callback) fileType = ExternalFileTypes.getInstance().getExternalFileTypeByExt(suffix); } String suggestedName = getSuggestedFileName(suffix); - List fDirectory = databaseContext.getFileDirectories(Globals.prefs.getFileDirectoryPreferences()); + List fDirectory = databaseContext.getFileDirectories(Globals.prefs.getFilePreferences()); String directory; if (fDirectory.isEmpty()) { directory = null; diff --git a/src/main/java/org/jabref/gui/externalfiles/DroppedFileHandler.java b/src/main/java/org/jabref/gui/externalfiles/DroppedFileHandler.java index 58bfb1e87f4..b4cc4e2e9bf 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DroppedFileHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/DroppedFileHandler.java @@ -147,9 +147,9 @@ public void handleDroppedfile(String fileName, ExternalFileType fileType, BibEnt String destFilename; if (linkInPlace.isSelected()) { - destFilename = FileUtil.shortenFileName(Paths.get(fileName), - panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())) - .toString(); + destFilename = FileUtil.relativize(Paths.get(fileName), + panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())) + .toString(); } else { destFilename = renameCheckBox.isSelected() ? renameToTextBox.getText() : Paths.get(fileName).toString(); if (copyRadioButton.isSelected()) { @@ -199,9 +199,9 @@ public void linkPdfToEntry(String fileName, BibEntry entry) { NamedCompound edits = new NamedCompound(Localization.lang("Drop %0", fileType.getExtension())); if (linkInPlace.isSelected()) { - destFilename = FileUtil.shortenFileName(Paths.get(fileName), - panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())) - .toString(); + destFilename = FileUtil.relativize(Paths.get(fileName), + panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())) + .toString(); } else { destFilename = renameCheckBox.isSelected() ? renameToTextBox.getText() : new File(fileName).getName(); if (copyRadioButton.isSelected()) { @@ -284,9 +284,9 @@ private boolean tryXmpImport(String fileName, ExternalFileType fileType, NamedCo String destFilename; if (linkInPlace.isSelected()) { - destFilename = FileUtil.shortenFileName(Paths.get(fileName), - panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())) - .toString(); + destFilename = FileUtil.relativize(Paths.get(fileName), + panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())) + .toString(); } else { if (renameCheckBox.isSelected() || (single == null)) { destFilename = fileName; @@ -325,7 +325,7 @@ private boolean showLinkMoveCopyRenameDialog(String linkFileName, ExternalFileTy String dialogTitle = Localization.lang("Link to file %0", linkFileName); Optional dir = panel.getBibDatabaseContext() - .getFirstExistingFileDir(Globals.prefs.getFileDirectoryPreferences()); + .getFirstExistingFileDir(Globals.prefs.getFilePreferences()); if (!dir.isPresent()) { destDirLabel.setText(Localization.lang("File directory is not set or does not exist!")); @@ -416,7 +416,7 @@ private void doLink(BibEntry entry, ExternalFileType fileType, String filename, if (avoidDuplicate) { // For comparison, find the absolute filename: List dirs = panel.getBibDatabaseContext() - .getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()); + .getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); String absFilename; if (new File(filename).isAbsolute() || dirs.isEmpty()) { absFilename = filename; @@ -475,7 +475,7 @@ private void doLink(BibEntry entry, ExternalFileType fileType, String filename, */ private boolean doMove(String fileName, String destFilename, NamedCompound edits) { Optional dir = panel.getBibDatabaseContext() - .getFirstExistingFileDir(Globals.prefs.getFileDirectoryPreferences()); + .getFirstExistingFileDir(Globals.prefs.getFilePreferences()); if (dir.isPresent()) { Path destFile = dir.get().resolve(destFilename); @@ -522,7 +522,7 @@ private boolean doMove(String fileName, String destFilename, NamedCompound edits private boolean doCopy(String fileName, String toFile, NamedCompound edits) { List dirs = panel.getBibDatabaseContext() - .getFileDirectories(Globals.prefs.getFileDirectoryPreferences()); + .getFileDirectories(Globals.prefs.getFilePreferences()); int found = -1; for (int i = 0; i < dirs.size(); i++) { if (new File(dirs.get(i)).exists()) { diff --git a/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java b/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java index 7d6e83c6921..98640b3a377 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java +++ b/src/main/java/org/jabref/gui/externalfiles/ExternalFilesEntryLinker.java @@ -14,26 +14,26 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; public class ExternalFilesEntryLinker { private final ExternalFileTypes externalFileTypes; - private final FileDirectoryPreferences fileDirectoryPreferences; + private final FilePreferences filePreferences; private final BibDatabaseContext bibDatabaseContext; private final MoveFilesCleanup moveFilesCleanup; private final RenamePdfCleanup renameFilesCleanup; - public ExternalFilesEntryLinker(ExternalFileTypes externalFileTypes, FileDirectoryPreferences fileDirectoryPreferences, String fileDirPattern, BibDatabaseContext bibDatabaseContext, String fileNamePattern) { + public ExternalFilesEntryLinker(ExternalFileTypes externalFileTypes, FilePreferences filePreferences, BibDatabaseContext bibDatabaseContext) { this.externalFileTypes = externalFileTypes; - this.fileDirectoryPreferences = fileDirectoryPreferences; + this.filePreferences = filePreferences; this.bibDatabaseContext = bibDatabaseContext; - this.moveFilesCleanup = new MoveFilesCleanup(bibDatabaseContext, fileDirPattern, fileDirectoryPreferences); - this.renameFilesCleanup = new RenamePdfCleanup(false, bibDatabaseContext, fileNamePattern, fileDirectoryPreferences); + this.moveFilesCleanup = new MoveFilesCleanup(bibDatabaseContext, filePreferences); + this.renameFilesCleanup = new RenamePdfCleanup(false, bibDatabaseContext, filePreferences); } public Optional copyFileToFileDir(Path file) { - Optional firstExistingFileDir = bibDatabaseContext.getFirstExistingFileDir(fileDirectoryPreferences); + Optional firstExistingFileDir = bibDatabaseContext.getFirstExistingFileDir(filePreferences); if (firstExistingFileDir.isPresent()) { Path targetFile = firstExistingFileDir.get().resolve(file.getFileName()); if (FileUtil.copyFile(file, targetFile, false)) { @@ -56,13 +56,12 @@ public void addFileToEntry(BibEntry entry, Path file) { } public void addFilesToEntry(BibEntry entry, List files) { - for (Path file : files) { FileUtil.getFileExtension(file).ifPresent(ext -> { ExternalFileType type = externalFileTypes.getExternalFileTypeByExt(ext) .orElse(new UnknownExternalFileType(ext)); - Path relativePath = FileUtil.shortenFileName(file, bibDatabaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences)); + Path relativePath = FileUtil.relativize(file, bibDatabaseContext.getFileDirectoriesAsPaths(filePreferences)); LinkedFile linkedfile = new LinkedFile("", relativePath.toString(), type.getName()); entry.addFile(linkedfile); }); diff --git a/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java index ef81d91e030..bb72fa8ff3f 100644 --- a/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/FindFullTextAction.java @@ -91,7 +91,7 @@ private void downloadFullTexts(Map, BibEntry> downloads) { BibEntry entry = download.getValue(); Optional result = download.getKey(); if (result.isPresent()) { - Optional dir = basePanel.getBibDatabaseContext().getFirstExistingFileDir(Globals.prefs.getFileDirectoryPreferences()); + Optional dir = basePanel.getBibDatabaseContext().getFirstExistingFileDir(Globals.prefs.getFilePreferences()); if (!dir.isPresent()) { diff --git a/src/main/java/org/jabref/gui/externalfiles/NewDroppedFileHandler.java b/src/main/java/org/jabref/gui/externalfiles/NewDroppedFileHandler.java index 58ab13155e5..77cfd682642 100644 --- a/src/main/java/org/jabref/gui/externalfiles/NewDroppedFileHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/NewDroppedFileHandler.java @@ -24,7 +24,7 @@ import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.JabRefPreferences; @@ -45,19 +45,17 @@ public class NewDroppedFileHandler { public NewDroppedFileHandler(DialogService dialogService, BibDatabaseContext bibDatabaseContext, ExternalFileTypes externalFileTypes, - FileDirectoryPreferences fileDirectoryPreferences, - String fileDirPattern, + FilePreferences filePreferences, ImportFormatPreferences importFormatPreferences, UpdateFieldPreferences updateFieldPreferences, - FileUpdateMonitor fileupdateMonitor, - String fileNamePattern) { + FileUpdateMonitor fileupdateMonitor) { this.dialogService = dialogService; this.bibDatabaseContext = bibDatabaseContext; this.updateFieldPreferences = updateFieldPreferences; this.fileUpdateMonitor = fileupdateMonitor; - this.linker = new ExternalFilesEntryLinker(externalFileTypes, fileDirectoryPreferences, fileDirPattern, bibDatabaseContext, fileNamePattern); + this.linker = new ExternalFilesEntryLinker(externalFileTypes, filePreferences, bibDatabaseContext); this.contentImporter = new ExternalFilesContentImporter(importFormatPreferences); } diff --git a/src/main/java/org/jabref/gui/externalfiles/TransferableFileLinkSelection.java b/src/main/java/org/jabref/gui/externalfiles/TransferableFileLinkSelection.java index ea3f0626f5b..798189cb1c6 100644 --- a/src/main/java/org/jabref/gui/externalfiles/TransferableFileLinkSelection.java +++ b/src/main/java/org/jabref/gui/externalfiles/TransferableFileLinkSelection.java @@ -31,8 +31,8 @@ public TransferableFileLinkSelection(BasePanel panel, List selection) if (!files.isEmpty()) { // Find the default directory for this field type, if any: LinkedFile firstFile = files.get(0); - firstFile.findIn(panel.getBibDatabaseContext(), Globals.prefs.getFileDirectoryPreferences()) - .ifPresent(fileList::add); + firstFile.findIn(panel.getBibDatabaseContext(), Globals.prefs.getFilePreferences()) + .ifPresent(fileList::add); } } diff --git a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java index c15a22c83ac..a2ebcb02dae 100644 --- a/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java +++ b/src/main/java/org/jabref/gui/externalfiletype/ExternalFileMenuItem.java @@ -57,7 +57,7 @@ public ExternalFileMenuItem(JabRefFrame frame, String name, String link, Icon ic public void actionPerformed(ActionEvent e) { boolean success = openLink(); if (!success) { - List searchedDirs = databaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()); + List searchedDirs = databaseContext.getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); frame.output(Localization.lang("Unable to open %0", link) + " " + Arrays.toString(searchedDirs.toArray())); } } diff --git a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java index cff53232371..9809e926cc8 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java +++ b/src/main/java/org/jabref/gui/fieldeditors/FieldEditors.java @@ -43,7 +43,7 @@ public static FieldEditorFX getForField(final String fieldName, final FieldCheckers fieldCheckers = new FieldCheckers( databaseContext, - preferences.getFileDirectoryPreferences(), + preferences.getFilePreferences(), journalAbbreviationRepository, preferences.getBoolean(JabRefPreferences.ENFORCE_LEGAL_BIBTEX_KEY)); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java index ee0a04e5b4f..3ef40844f14 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFileViewModel.java @@ -34,17 +34,15 @@ import org.jabref.gui.icon.JabRefIcon; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; -import org.jabref.logic.cleanup.MoveFilesCleanup; -import org.jabref.logic.cleanup.RenamePdfCleanup; +import org.jabref.logic.externalfiles.LinkedFileHandler; import org.jabref.logic.l10n.Localization; import org.jabref.logic.net.URLDownload; -import org.jabref.logic.util.io.FileUtil; import org.jabref.logic.xmp.XmpPreferences; import org.jabref.logic.xmp.XmpUtilWriter; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.strings.StringUtil; import org.jabref.preferences.JabRefPreferences; @@ -64,10 +62,9 @@ public class LinkedFileViewModel extends AbstractViewModel { private final DialogService dialogService; private final BibEntry entry; private final TaskExecutor taskExecutor; - private final FileDirectoryPreferences fileDirectoryPreferences; - private final String fileDirPattern; + private final FilePreferences filePreferences; private final XmpPreferences xmpPreferences; - private final String fileNamePattern; + private final LinkedFileHandler linkedFileHandler; public LinkedFileViewModel(LinkedFile linkedFile, BibEntry entry, @@ -77,15 +74,14 @@ public LinkedFileViewModel(LinkedFile linkedFile, JabRefPreferences preferences) { this.linkedFile = linkedFile; + this.filePreferences = preferences.getFilePreferences(); + this.linkedFileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, filePreferences); this.databaseContext = databaseContext; this.entry = entry; this.dialogService = dialogService; this.taskExecutor = taskExecutor; xmpPreferences = preferences.getXMPPreferences(); - fileNamePattern = preferences.get(JabRefPreferences.IMPORT_FILENAMEPATTERN); - fileDirectoryPreferences = preferences.getFileDirectoryPreferences(); - fileDirPattern = preferences.get(JabRefPreferences.IMPORT_FILEDIRPATTERN); downloadOngoing.bind(downloadProgress.greaterThanOrEqualTo(0).and(downloadProgress.lessThan(1))); canWriteXMPMetadata.setValue(!linkedFile.isOnlineLink() && linkedFile.getFileType().equalsIgnoreCase("pdf")); } @@ -178,7 +174,7 @@ public void openFolder() { path = Paths.get(linkedFile.getLink()); } else { // relative to file folder - for (Path folder : databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences)) { + for (Path folder : databaseContext.getFileDirectoriesAsPaths(filePreferences)) { Path file = folder.resolve(linkedFile.getLink()); if (Files.exists(file)) { path = file; @@ -202,41 +198,43 @@ public void rename() { return; } - Optional file = linkedFile.findIn(databaseContext, fileDirectoryPreferences); - if ((file.isPresent()) && Files.exists(file.get())) { - RenamePdfCleanup pdfCleanup = new RenamePdfCleanup(false, databaseContext, fileDirPattern, fileDirectoryPreferences, linkedFile); - performRenameWithConflictCheck(file.get(), pdfCleanup); + Optional file = linkedFile.findIn(databaseContext, filePreferences); + if (file.isPresent()) { + performRenameWithConflictCheck(); } else { dialogService.showErrorDialogAndWait(Localization.lang("File not found"), Localization.lang("Could not find file '%0'.", linkedFile.getLink())); } } - private void performRenameWithConflictCheck(Path file, RenamePdfCleanup pdfCleanup) { - boolean confirm; - Optional fileConflictCheck = pdfCleanup.findExistingFile(linkedFile, entry); - if (!fileConflictCheck.isPresent()) { - try { - pdfCleanup.cleanupWithException(entry); - } catch (IOException e) { - dialogService.showErrorDialogAndWait(Localization.lang("Rename failed"), Localization.lang("JabRef cannot access the file because it is being used by another process.")); - } - } else { - String targetFileName = pdfCleanup.getTargetFileName(linkedFile, entry); - confirm = dialogService.showConfirmationDialogAndWait(Localization.lang("File exists"), - Localization.lang("'%0' exists. Overwrite file?", targetFileName), - Localization.lang("Overwrite"), - Localization.lang("Cancel")); + private void performRenameWithConflictCheck() { + Optional fileConflictCheck = linkedFileHandler.findExistingFile(linkedFile, entry); + if (fileConflictCheck.isPresent()) { + String targetFileName = linkedFileHandler.getSuggestedFileName(); + boolean confirmOverwrite = dialogService.showConfirmationDialogAndWait( + Localization.lang("File exists"), + Localization.lang("'%0' exists. Overwrite file?", targetFileName), + Localization.lang("Overwrite")); - if (confirm) { + if (confirmOverwrite) { try { - FileUtil.renameFileWithException(fileConflictCheck.get(), file, true); - pdfCleanup.cleanupWithException(entry); + Files.delete(fileConflictCheck.get()); } catch (IOException e) { - dialogService.showErrorDialogAndWait(Localization.lang("Rename failed"), - Localization.lang("JabRef cannot access the file because it is being used by another process.")); + dialogService.showErrorDialogAndWait( + Localization.lang("Rename failed"), + Localization.lang("JabRef cannot access the file because it is being used by another process."), + e); + return; } + } else { + return; } } + + try { + linkedFileHandler.renameToSuggestedName(); + } catch (IOException e) { + dialogService.showErrorDialogAndWait(Localization.lang("Rename failed"), Localization.lang("JabRef cannot access the file because it is being used by another process.")); + } } public void moveToDefaultDirectory() { @@ -246,17 +244,23 @@ public void moveToDefaultDirectory() { } // Get target folder - Optional fileDir = databaseContext.getFirstExistingFileDir(fileDirectoryPreferences); + Optional fileDir = databaseContext.getFirstExistingFileDir(filePreferences); if (!fileDir.isPresent()) { dialogService.showErrorDialogAndWait(Localization.lang("Move file"), Localization.lang("File directory is not set or does not exist!")); return; } - Optional file = linkedFile.findIn(databaseContext, fileDirectoryPreferences); - if ((file.isPresent()) && Files.exists(file.get())) { + Optional file = linkedFile.findIn(databaseContext, filePreferences); + if ((file.isPresent())) { // Found the linked file, so move it - MoveFilesCleanup moveFiles = new MoveFilesCleanup(databaseContext, fileDirPattern, fileDirectoryPreferences, linkedFile); - moveFiles.cleanup(entry); + try { + linkedFileHandler.moveToDefaultDirectory(); + } catch (IOException exception) { + dialogService.showErrorDialogAndWait( + Localization.lang("Move file"), + Localization.lang("Could not move file '%0'.", file.get().toString()), + exception); + } } else { // File doesn't exist, so we can't move it. dialogService.showErrorDialogAndWait(Localization.lang("File not found"), Localization.lang("Could not find file '%0'.", linkedFile.getLink())); @@ -268,8 +272,14 @@ public void moveToDefaultDirectoryAndRename() { rename(); } - public boolean delete(FileDirectoryPreferences prefs) { - Optional file = linkedFile.findIn(databaseContext, prefs); + /** + * Asks the user for confirmation that he really wants to the delete the file from disk (or just remove the link). + * + * @return true if the linked file should be removed afterwards from the entry (i.e because it was deleted + * successfully, does not exist in the first place or the user choose to remove it) + */ + public boolean delete() { + Optional file = linkedFile.findIn(databaseContext, filePreferences); if (!file.isPresent()) { LOGGER.warn("Could not find file " + linkedFile.getLink()); @@ -279,9 +289,9 @@ public boolean delete(FileDirectoryPreferences prefs) { ButtonType removeFromEntry = new ButtonType(Localization.lang("Remove from entry"), ButtonData.YES); ButtonType deleteFromEntry = new ButtonType(Localization.lang("Delete from disk")); Optional buttonType = dialogService.showCustomButtonDialogAndWait(AlertType.INFORMATION, - Localization.lang("Delete '%0'", file.get().toString()), - Localization.lang("Delete the selected file permanently from disk, or just remove the file from the entry? Pressing Delete will delete the file permanently from disk."), - removeFromEntry, deleteFromEntry, ButtonType.CANCEL); + Localization.lang("Delete '%0'", file.get().toString()), + Localization.lang("Delete the selected file permanently from disk, or just remove the file from the entry? Pressing Delete will delete the file permanently from disk."), + removeFromEntry, deleteFromEntry, ButtonType.CANCEL); if (buttonType.isPresent()) { if (buttonType.get().equals(removeFromEntry)) { @@ -316,7 +326,7 @@ public void edit() { public void writeXMPMetadata() { // Localization.lang("Writing XMP-metadata...") BackgroundTask writeTask = BackgroundTask.wrap(() -> { - Optional file = linkedFile.findIn(databaseContext, fileDirectoryPreferences); + Optional file = linkedFile.findIn(databaseContext, filePreferences); if (!file.isPresent()) { // TODO: Print error message // Localization.lang("PDF does not exist"); @@ -343,7 +353,7 @@ public void download() { } try { - Optional targetDirectory = databaseContext.getFirstExistingFileDir(fileDirectoryPreferences); + Optional targetDirectory = databaseContext.getFirstExistingFileDir(filePreferences); if (!targetDirectory.isPresent()) { dialogService.showErrorDialogAndWait(Localization.lang("Download file"), Localization.lang("File directory is not set or does not exist!")); return; @@ -356,13 +366,12 @@ public void download() { String suggestedTypeName = suggestedType.map(ExternalFileType::getName).orElse(""); linkedFile.setFileType(suggestedTypeName); - String suffix = suggestedType.map(ExternalFileType::getExtension).orElse(""); - String suggestedName = getSuggestedFileName(suffix); + String suggestedName = linkedFileHandler.getSuggestedFileName(); return targetDirectory.get().resolve(suggestedName); }) .then(destination -> new FileDownloadTask(urlDownload.getSource(), destination)) .onSuccess(destination -> { - LinkedFile newLinkedFile = LinkedFilesEditorViewModel.fromFile(destination, databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences)); + LinkedFile newLinkedFile = LinkedFilesEditorViewModel.fromFile(destination, databaseContext.getFileDirectoriesAsPaths(filePreferences)); linkedFile.setLink(newLinkedFile.getLink()); linkedFile.setFileType(newLinkedFile.getFileType()); }) @@ -405,15 +414,6 @@ private Optional inferFileTypeFromURL(String url) { } } - private String getSuggestedFileName(String suffix) { - String plannedName = FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, fileNamePattern); - - if (!suffix.isEmpty()) { - plannedName += "." + suffix; - } - return plannedName; - } - public LinkedFile getFile() { return linkedFile; } diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java index b215e97a4ad..815d4be3a04 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java @@ -91,12 +91,12 @@ public static LinkedFile fromFile(Path file, List fileDirectories) { ExternalFileType suggestedFileType = ExternalFileTypes.getInstance() .getExternalFileTypeByExt(fileExtension) .orElse(new UnknownExternalFileType(fileExtension)); - Path relativePath = FileUtil.shortenFileName(file, fileDirectories); + Path relativePath = FileUtil.relativize(file, fileDirectories); return new LinkedFile("", relativePath.toString(), suggestedFileType.getName()); } public LinkedFileViewModel fromFile(Path file) { - List fileDirectories = databaseContext.getFileDirectoriesAsPaths(preferences.getFileDirectoryPreferences()); + List fileDirectories = databaseContext.getFileDirectoriesAsPaths(preferences.getFilePreferences()); LinkedFile linkedFile = fromFile(file, fileDirectories); return new LinkedFileViewModel(linkedFile, entry, databaseContext, taskExecutor, dialogService, preferences); @@ -126,14 +126,14 @@ public ListProperty filesProperty() { } public void addNewFile() { - Path workingDirectory = databaseContext.getFirstExistingFileDir(preferences.getFileDirectoryPreferences()) + Path workingDirectory = databaseContext.getFirstExistingFileDir(preferences.getFilePreferences()) .orElse(Paths.get(preferences.get(JabRefPreferences.WORKING_DIRECTORY))); FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder() .withInitialDirectory(workingDirectory) .build(); - List fileDirectories = databaseContext.getFileDirectoriesAsPaths(preferences.getFileDirectoryPreferences()); + List fileDirectories = databaseContext.getFileDirectoriesAsPaths(preferences.getFilePreferences()); dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(newFile -> { LinkedFile newLinkedFile = fromFile(newFile, fileDirectories); files.add(new LinkedFileViewModel(newLinkedFile, entry, databaseContext, taskExecutor, dialogService, preferences)); @@ -158,7 +158,7 @@ public void bindToEntry(BibEntry entry) { private List findAssociatedNotLinkedFiles(BibEntry entry) { List result = new ArrayList<>(); - AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(databaseContext, preferences.getFileDirectoryPreferences(), preferences.getAutoLinkPreferences(), ExternalFileTypes.getInstance()); + AutoSetFileLinksUtil util = new AutoSetFileLinksUtil(databaseContext, preferences.getFilePreferences(), preferences.getAutoLinkPreferences(), ExternalFileTypes.getInstance()); try { List linkedFiles = util.findAssociatedNotLinkedFiles(entry); for (LinkedFile linkedFile : linkedFiles) { @@ -214,7 +214,7 @@ public void deleteFile(LinkedFileViewModel file) { if (file.getFile().isOnlineLink()) { removeFileLink(file); } else { - boolean deleteSuccessful = file.delete(preferences.getFileDirectoryPreferences()); + boolean deleteSuccessful = file.delete(); if (deleteSuccessful) { files.remove(file); } diff --git a/src/main/java/org/jabref/gui/filelist/AttachFileAction.java b/src/main/java/org/jabref/gui/filelist/AttachFileAction.java index 47e89416858..c36318ea60e 100644 --- a/src/main/java/org/jabref/gui/filelist/AttachFileAction.java +++ b/src/main/java/org/jabref/gui/filelist/AttachFileAction.java @@ -38,7 +38,7 @@ public void execute() { dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(newFile -> { - LinkedFile linkedFile = LinkedFilesEditorViewModel.fromFile(newFile, panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())); + LinkedFile linkedFile = LinkedFilesEditorViewModel.fromFile(newFile, panel.getBibDatabaseContext().getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())); LinkedFileEditDialogView dialog = new LinkedFileEditDialogView(linkedFile); diff --git a/src/main/java/org/jabref/gui/filelist/FileListEntryEditor.java b/src/main/java/org/jabref/gui/filelist/FileListEntryEditor.java index 31c046720f0..fc590f146e2 100644 --- a/src/main/java/org/jabref/gui/filelist/FileListEntryEditor.java +++ b/src/main/java/org/jabref/gui/filelist/FileListEntryEditor.java @@ -86,7 +86,7 @@ public class FileListEntryEditor { private final ActionListener browsePressed = e -> { String fileText = link.getText().trim(); Optional file = FileHelper.expandFilename(this.databaseContext, fileText, - Globals.prefs.getFileDirectoryPreferences()); + Globals.prefs.getFilePreferences()); Path workingDir = file.orElse(Paths.get(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY))); String fileName = Paths.get(fileText).getFileName().toString(); @@ -108,8 +108,8 @@ public class FileListEntryEditor { // If the file is below the file directory, make the path relative: List fileDirectories = this.databaseContext - .getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()); - newFile = FileUtil.shortenFileName(newFile, fileDirectories); + .getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); + newFile = FileUtil.relativize(newFile, fileDirectories); link.setText(newFile.toString()); link.requestFocus(); @@ -346,7 +346,7 @@ private void storeSettings(LinkedFile listEntry) { String fileLink = ""; // See if we should trim the file link to be relative to the file directory: try { - List dirs = databaseContext.getFileDirectories(Globals.prefs.getFileDirectoryPreferences()); + List dirs = databaseContext.getFileDirectories(Globals.prefs.getFilePreferences()); if (dirs.isEmpty()) { fileLink = this.link.getText().trim(); } else { diff --git a/src/main/java/org/jabref/gui/filelist/LinkedFilesEditDialogViewModel.java b/src/main/java/org/jabref/gui/filelist/LinkedFilesEditDialogViewModel.java index 94c534381c1..96005fc6b57 100644 --- a/src/main/java/org/jabref/gui/filelist/LinkedFilesEditDialogViewModel.java +++ b/src/main/java/org/jabref/gui/filelist/LinkedFilesEditDialogViewModel.java @@ -70,7 +70,7 @@ private void setExternalFileTypeByExtension(String link) { public void openBrowseDialog() { String fileText = link.get(); - Optional file = FileHelper.expandFilename(database, fileText, preferences.getFileDirectoryPreferences()); + Optional file = FileHelper.expandFilename(database, fileText, preferences.getFilePreferences()); Path workingDir = file.orElse(preferences.getWorkingDir()); String fileName = Paths.get(fileText).getFileName().toString(); @@ -127,8 +127,8 @@ public LinkedFile getNewLinkedFile() { } private String relativize(Path filePath) { - List fileDirectories = database.getFileDirectoriesAsPaths(preferences.getFileDirectoryPreferences()); - return FileUtil.shortenFileName(filePath, fileDirectories).toString(); + List fileDirectories = database.getFileDirectoriesAsPaths(preferences.getFilePreferences()); + return FileUtil.relativize(filePath, fileDirectories).toString(); } } diff --git a/src/main/java/org/jabref/gui/importer/EntryFromFileCreator.java b/src/main/java/org/jabref/gui/importer/EntryFromFileCreator.java index dd83b21d4ee..80264c77c00 100644 --- a/src/main/java/org/jabref/gui/importer/EntryFromFileCreator.java +++ b/src/main/java/org/jabref/gui/importer/EntryFromFileCreator.java @@ -136,8 +136,8 @@ private void addFileInfo(BibEntry entry, File file) { .getExternalFileTypeByExt(externalFileType.getFieldName()); List possibleFilePaths = JabRefGUI.getMainFrame().getCurrentBasePanel().getBibDatabaseContext() - .getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()); - Path shortenedFileName = FileUtil.shortenFileName(file.toPath(), possibleFilePaths); + .getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); + Path shortenedFileName = FileUtil.relativize(file.toPath(), possibleFilePaths); FileListEntry fileListEntry = new FileListEntry("", shortenedFileName.toString(), fileType); FileListTableModel model = new FileListTableModel(); diff --git a/src/main/java/org/jabref/gui/importer/UnlinkedPDFFileFilter.java b/src/main/java/org/jabref/gui/importer/UnlinkedPDFFileFilter.java index 9686a8af9ae..e387a6bd758 100644 --- a/src/main/java/org/jabref/gui/importer/UnlinkedPDFFileFilter.java +++ b/src/main/java/org/jabref/gui/importer/UnlinkedPDFFileFilter.java @@ -26,7 +26,7 @@ public class UnlinkedPDFFileFilter implements FileFilter { public UnlinkedPDFFileFilter(FileFilter fileFilter, BibDatabaseContext databaseContext) { this.fileFilter = fileFilter; - this.lookup = new DatabaseFileLookup(databaseContext, Globals.prefs.getFileDirectoryPreferences()); + this.lookup = new DatabaseFileLookup(databaseContext, Globals.prefs.getFilePreferences()); } @Override diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 4678e6dfb30..0c739e23b6a 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -73,12 +73,10 @@ public MainTable(MainTableDataModel model, JabRefFrame frame, this.undoManager = panel.getUndoManager(); fileHandler = new NewDroppedFileHandler(frame.getDialogService(), database, externalFileTypes, - Globals.prefs.getFileDirectoryPreferences(), - Globals.prefs.getCleanupPreferences(Globals.journalAbbreviationLoader).getFileDirPattern(), + Globals.prefs.getFilePreferences(), Globals.prefs.getImportFormatPreferences(), Globals.prefs.getUpdateFieldPreferences(), - Globals.getFileUpdateMonitor(), - Globals.prefs.get(JabRefPreferences.IMPORT_FILENAMEPATTERN) + Globals.getFileUpdateMonitor() ); diff --git a/src/main/java/org/jabref/gui/preferences/FileTab.java b/src/main/java/org/jabref/gui/preferences/FileTab.java index 5369f8df138..110caa3f640 100644 --- a/src/main/java/org/jabref/gui/preferences/FileTab.java +++ b/src/main/java/org/jabref/gui/preferences/FileTab.java @@ -22,7 +22,7 @@ import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.OS; import org.jabref.model.entry.FieldName; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.preferences.JabRefPreferences; /** @@ -152,7 +152,7 @@ public FileTab(DialogService dialogService, JabRefPreferences prefs) { @Override public void setValues() { - fileDir.setText(prefs.getAsOptional(FieldName.FILE + FileDirectoryPreferences.DIR_SUFFIX).orElse("")); + fileDir.setText(prefs.getAsOptional(FieldName.FILE + FilePreferences.DIR_SUFFIX).orElse("")); bibLocAsPrimaryDir.setSelected(prefs.getBoolean(JabRefPreferences.BIB_LOC_AS_PRIMARY_DIR)); runAutoFileSearch.setSelected(prefs.getBoolean(JabRefPreferences.RUN_AUTOMATIC_FILE_SEARCH)); allowFileAutoOpenBrowse.setSelected(prefs.getBoolean(JabRefPreferences.ALLOW_FILE_AUTO_OPEN_BROWSE)); @@ -193,7 +193,7 @@ public Node getBuilder() { @Override public void storeSettings() { - prefs.put(FieldName.FILE + FileDirectoryPreferences.DIR_SUFFIX, fileDir.getText()); + prefs.put(FieldName.FILE + FilePreferences.DIR_SUFFIX, fileDir.getText()); prefs.putBoolean(JabRefPreferences.BIB_LOC_AS_PRIMARY_DIR, bibLocAsPrimaryDir.isSelected()); prefs.putBoolean(JabRefPreferences.RUN_AUTOMATIC_FILE_SEARCH, runAutoFileSearch.isSelected()); prefs.putBoolean(JabRefPreferences.ALLOW_FILE_AUTO_OPEN_BROWSE, allowFileAutoOpenBrowse.isSelected()); diff --git a/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java b/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java index d00d15fba8a..51b85ba2784 100644 --- a/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java +++ b/src/main/java/org/jabref/gui/worker/SendAsEMailAction.java @@ -90,7 +90,7 @@ private String sendEmail() throws Exception { boolean openFolders = JabRefPreferences.getInstance().getBoolean(JabRefPreferences.OPEN_FOLDERS_OF_ATTACHED_FILES); List fileList = FileUtil.getListOfLinkedFiles(bes, frame.getCurrentBasePanel().getBibDatabaseContext() - .getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences())); + .getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences())); for (Path f : fileList) { attachments.add(f.toAbsolutePath().toString()); if (openFolders) { diff --git a/src/main/java/org/jabref/logic/cleanup/CleanupPreferences.java b/src/main/java/org/jabref/logic/cleanup/CleanupPreferences.java index ac5f96d6a48..5f548e16edf 100644 --- a/src/main/java/org/jabref/logic/cleanup/CleanupPreferences.java +++ b/src/main/java/org/jabref/logic/cleanup/CleanupPreferences.java @@ -1,37 +1,23 @@ package org.jabref.logic.cleanup; import org.jabref.logic.layout.LayoutFormatterPreferences; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; public class CleanupPreferences { - private final String fileNamePattern; - private final String fileDirPattern; private final LayoutFormatterPreferences layoutFormatterPreferences; - private final FileDirectoryPreferences fileDirectoryPreferences; + private final FilePreferences filePreferences; - - public CleanupPreferences(String fileNamePattern, String fileDirPattern, - LayoutFormatterPreferences layoutFormatterPreferences, FileDirectoryPreferences fileDirectoryPreferences) { - this.fileNamePattern = fileNamePattern; - this.fileDirPattern = fileDirPattern; + public CleanupPreferences(LayoutFormatterPreferences layoutFormatterPreferences, FilePreferences filePreferences) { this.layoutFormatterPreferences = layoutFormatterPreferences; - this.fileDirectoryPreferences = fileDirectoryPreferences; - } - - public String getFileNamePattern() { - return fileNamePattern; - } - - public String getFileDirPattern() { - return fileDirPattern; + this.filePreferences = filePreferences; } public LayoutFormatterPreferences getLayoutFormatterPreferences() { return layoutFormatterPreferences; } - public FileDirectoryPreferences getFileDirectoryPreferences() { - return fileDirectoryPreferences; + public FilePreferences getFilePreferences() { + return filePreferences; } } diff --git a/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java b/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java index 737a16a0157..c07650436d1 100644 --- a/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java +++ b/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java @@ -8,26 +8,16 @@ import org.jabref.model.cleanup.CleanupJob; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; public class CleanupWorker { private final BibDatabaseContext databaseContext; - private final String fileNamePattern; - private final String fileDirPattern; - private final FileDirectoryPreferences fileDirectoryPreferences; - private int unsuccessfulRenames; - + private final FilePreferences filePreferences; public CleanupWorker(BibDatabaseContext databaseContext, CleanupPreferences cleanupPreferences) { this.databaseContext = databaseContext; - this.fileNamePattern = cleanupPreferences.getFileNamePattern(); - this.fileDirPattern = cleanupPreferences.getFileDirPattern(); - this.fileDirectoryPreferences = cleanupPreferences.getFileDirectoryPreferences(); - } - - public int getUnsuccessfulRenames() { - return unsuccessfulRenames; + this.filePreferences = cleanupPreferences.getFilePreferences(); } public List cleanup(CleanupPreset preset, BibEntry entry) { @@ -69,16 +59,14 @@ private List determineCleanupActions(CleanupPreset preset) { jobs.add(new FileLinksCleanup()); } if (preset.isMovePDF()) { - jobs.add(new MoveFilesCleanup(databaseContext, fileDirPattern, fileDirectoryPreferences)); + jobs.add(new MoveFilesCleanup(databaseContext, filePreferences)); } if (preset.isMakePathsRelative()) { - jobs.add(new RelativePathsCleanup(databaseContext, fileDirectoryPreferences)); + jobs.add(new RelativePathsCleanup(databaseContext, filePreferences)); } if (preset.isRenamePDF()) { - RenamePdfCleanup cleaner = new RenamePdfCleanup(preset.isRenamePdfOnlyRelativePaths(), databaseContext, - fileNamePattern, fileDirectoryPreferences); + RenamePdfCleanup cleaner = new RenamePdfCleanup(preset.isRenamePdfOnlyRelativePaths(), databaseContext, filePreferences); jobs.add(cleaner); - unsuccessfulRenames += cleaner.getUnsuccessfulRenames(); } return jobs; diff --git a/src/main/java/org/jabref/logic/cleanup/MoveFilesCleanup.java b/src/main/java/org/jabref/logic/cleanup/MoveFilesCleanup.java index 463dd0aba53..420167764f8 100644 --- a/src/main/java/org/jabref/logic/cleanup/MoveFilesCleanup.java +++ b/src/main/java/org/jabref/logic/cleanup/MoveFilesCleanup.java @@ -1,25 +1,19 @@ package org.jabref.logic.cleanup; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import org.jabref.logic.util.io.FileUtil; +import org.jabref.logic.externalfiles.LinkedFileHandler; import org.jabref.model.FieldChange; import org.jabref.model.cleanup.CleanupJob; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; -import org.jabref.model.util.FileHelper; +import org.jabref.model.metadata.FilePreferences; +import org.jabref.model.util.OptionalUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,106 +21,35 @@ public class MoveFilesCleanup implements CleanupJob { private static final Logger LOGGER = LoggerFactory.getLogger(MoveFilesCleanup.class); - private final BibDatabaseContext databaseContext; - private final FileDirectoryPreferences fileDirectoryPreferences; - - private final String fileDirPattern; - private LinkedFile singleFileFieldCleanup; + private final BibDatabaseContext databaseContext; + private final FilePreferences filePreferences; - public MoveFilesCleanup(BibDatabaseContext databaseContext, String fileDirPattern, - FileDirectoryPreferences fileDirectoryPreferences) { + public MoveFilesCleanup(BibDatabaseContext databaseContext, FilePreferences filePreferences) { this.databaseContext = Objects.requireNonNull(databaseContext); - this.fileDirPattern = Objects.requireNonNull(fileDirPattern); - this.fileDirectoryPreferences = Objects.requireNonNull(fileDirectoryPreferences); - } - - public MoveFilesCleanup(BibDatabaseContext databaseContext, String fileDirPattern, - FileDirectoryPreferences fileDirectoryPreferences, LinkedFile field) { - - this(databaseContext, fileDirPattern, fileDirectoryPreferences); - this.singleFileFieldCleanup = field; + this.filePreferences = Objects.requireNonNull(filePreferences); } @Override public List cleanup(BibEntry entry) { - Optional firstExistingFileDir = databaseContext.getFirstExistingFileDir(fileDirectoryPreferences); - - if (!firstExistingFileDir.isPresent()) { - return Collections.emptyList(); - } - - List paths = databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences); - String defaultFileDirectory = firstExistingFileDir.get().toString(); - Optional targetDirectory = FileHelper.expandFilenameAsPath(defaultFileDirectory, paths); - - if (!targetDirectory.isPresent()) { - return Collections.emptyList(); - } - - List fileList; - List newFileList; - - if (singleFileFieldCleanup != null) { - fileList = Arrays.asList(singleFileFieldCleanup); - //Add all other except the current selected file - newFileList = entry.getFiles().stream().filter(name -> !name.equals(singleFileFieldCleanup)) - .collect(Collectors.toList()); - } else { - newFileList = new ArrayList<>(); - fileList = entry.getFiles(); - } + List files = entry.getFiles(); boolean changed = false; - for (LinkedFile fileEntry : fileList) { - String oldFileName = fileEntry.getLink(); - - Optional oldFile = fileEntry.findIn(paths); - if (!oldFile.isPresent() || !Files.exists(oldFile.get())) { - newFileList.add(fileEntry); - continue; - } - String targetDirName = ""; - if (!fileDirPattern.isEmpty()) { - targetDirName = FileUtil.createDirNameFromPattern(databaseContext.getDatabase(), entry, fileDirPattern); - } - - Path newTargetFile = targetDirectory.get().resolve(targetDirName).resolve(oldFile.get().getFileName()); - if (Files.exists(newTargetFile)) { - // We do not overwrite already existing files - newFileList.add(fileEntry); - continue; - } - + for (LinkedFile file : files) { + LinkedFileHandler fileHandler = new LinkedFileHandler(file, entry, databaseContext, filePreferences); try { - if (!Files.exists(newTargetFile)) { - Files.createDirectories(newTargetFile); - } - } catch (IOException e) { - LOGGER.error("Could no create necessary target directoires for renaming", e); - } - - if (FileUtil.renameFile(oldFile.get(), newTargetFile, true)) { - changed = true; - - String newEntryFilePath = Paths.get(defaultFileDirectory).relativize(newTargetFile).toString(); - LinkedFile newFileEntry = fileEntry; - if (!oldFileName.equals(newTargetFile.toString())) { - newFileEntry = new LinkedFile(fileEntry.getDescription(), newEntryFilePath, - fileEntry.getFileType()); + boolean fileChanged = fileHandler.moveToDefaultDirectory(); + if (fileChanged) { changed = true; } - newFileList.add(newFileEntry); + } catch (IOException exception) { + LOGGER.error("Error while moving file {}", file.getLink(), exception); } } if (changed) { - Optional change = entry.setFiles(newFileList); - if (change.isPresent()) { - return Collections.singletonList(change.get()); - } else { - return Collections.emptyList(); - } + Optional changes = entry.setFiles(files); + return OptionalUtil.toList(changes); } return Collections.emptyList(); diff --git a/src/main/java/org/jabref/logic/cleanup/RelativePathsCleanup.java b/src/main/java/org/jabref/logic/cleanup/RelativePathsCleanup.java index 305bdd58b55..9b18e939782 100644 --- a/src/main/java/org/jabref/logic/cleanup/RelativePathsCleanup.java +++ b/src/main/java/org/jabref/logic/cleanup/RelativePathsCleanup.java @@ -13,17 +13,16 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; public class RelativePathsCleanup implements CleanupJob { private final BibDatabaseContext databaseContext; - private final FileDirectoryPreferences fileDirectoryPreferences; + private final FilePreferences filePreferences; - - public RelativePathsCleanup(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences) { + public RelativePathsCleanup(BibDatabaseContext databaseContext, FilePreferences filePreferences) { this.databaseContext = Objects.requireNonNull(databaseContext); - this.fileDirectoryPreferences = Objects.requireNonNull(fileDirectoryPreferences); + this.filePreferences = Objects.requireNonNull(filePreferences); } @Override @@ -35,7 +34,7 @@ public List cleanup(BibEntry entry) { for (LinkedFile fileEntry : fileList) { String oldFileName = fileEntry.getLink(); String newFileName = FileUtil - .shortenFileName(Paths.get(oldFileName), databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences)) + .relativize(Paths.get(oldFileName), databaseContext.getFileDirectoriesAsPaths(filePreferences)) .toString(); LinkedFile newFileEntry = fileEntry; diff --git a/src/main/java/org/jabref/logic/cleanup/RenamePdfCleanup.java b/src/main/java/org/jabref/logic/cleanup/RenamePdfCleanup.java index 12061c6600b..4016081e60b 100644 --- a/src/main/java/org/jabref/logic/cleanup/RenamePdfCleanup.java +++ b/src/main/java/org/jabref/logic/cleanup/RenamePdfCleanup.java @@ -1,26 +1,20 @@ package org.jabref.logic.cleanup; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.jabref.logic.util.io.FileUtil; +import org.jabref.logic.externalfiles.LinkedFileHandler; import org.jabref.model.FieldChange; import org.jabref.model.cleanup.CleanupJob; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; -import org.jabref.model.strings.StringUtil; -import org.jabref.model.util.FileHelper; +import org.jabref.model.metadata.FilePreferences; +import org.jabref.model.util.OptionalUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,163 +24,40 @@ public class RenamePdfCleanup implements CleanupJob { private final BibDatabaseContext databaseContext; private final boolean onlyRelativePaths; - private final String fileNamePattern; - private final FileDirectoryPreferences fileDirectoryPreferences; - private int unsuccessfulRenames; - private LinkedFile singleFieldCleanup; + private final FilePreferences filePreferences; - - public RenamePdfCleanup(boolean onlyRelativePaths, BibDatabaseContext databaseContext, String fileNamePattern, - FileDirectoryPreferences fileDirectoryPreferences) { + public RenamePdfCleanup(boolean onlyRelativePaths, BibDatabaseContext databaseContext, FilePreferences filePreferences) { this.databaseContext = Objects.requireNonNull(databaseContext); this.onlyRelativePaths = onlyRelativePaths; - this.fileNamePattern = Objects.requireNonNull(fileNamePattern); - this.fileDirectoryPreferences = fileDirectoryPreferences; - } - - public RenamePdfCleanup(boolean onlyRelativePaths, BibDatabaseContext databaseContext, String fileNamePattern, - FileDirectoryPreferences fileDirectoryPreferences, LinkedFile singleField) { - - this(onlyRelativePaths, databaseContext, fileNamePattern, fileDirectoryPreferences); - this.singleFieldCleanup = singleField; + this.filePreferences = filePreferences; } @Override public List cleanup(BibEntry entry) { - try { - return cleanupWithException(entry); - } catch (IOException e) { - LOGGER.error("Cleanup failed", e); - return Collections.emptyList(); - } - } - - public List cleanupWithException(BibEntry entry) throws IOException { - List newFileList; - List oldFileList; - if (singleFieldCleanup != null) { - oldFileList = Collections.singletonList(singleFieldCleanup); - - newFileList = entry.getFiles().stream().filter(x -> !x.equals(singleFieldCleanup)) - .collect(Collectors.toList()); - } else { - newFileList = new ArrayList<>(); - oldFileList = entry.getFiles(); - } + List files = entry.getFiles(); boolean changed = false; - - for (LinkedFile oldLinkedFile : oldFileList) { - String realOldFilename = oldLinkedFile.getLink(); - - if (StringUtil.isBlank(realOldFilename)) { - continue; //Skip empty filenames - } - - if (onlyRelativePaths && Paths.get(realOldFilename).isAbsolute()) { - newFileList.add(oldLinkedFile); - continue; - } - - //old path and old filename - Optional expandedOldFile = oldLinkedFile.findIn(databaseContext, fileDirectoryPreferences); - - if ((!expandedOldFile.isPresent()) || (expandedOldFile.get().getParent() == null)) { - // something went wrong. Just skip this entry - newFileList.add(oldLinkedFile); - continue; - } - String targetFileName = getTargetFileName(oldLinkedFile, entry); - Path newPath = expandedOldFile.get().getParent().resolve(targetFileName); - - String expandedOldFilePath = expandedOldFile.get().toString(); - boolean pathsDifferOnlyByCase = newPath.toString().equalsIgnoreCase(expandedOldFilePath) - && !newPath.toString().equals(expandedOldFilePath); - - if (Files.exists(newPath) && !pathsDifferOnlyByCase) { - // we do not overwrite files - // Since File.exists is sometimes not case-sensitive, the check pathsDifferOnlyByCase ensures that we - // nonetheless rename files to a new name which just differs by case. - // TODO: we could check here if the newPath file is linked with the current entry. And if not, we could add a link - LOGGER.debug("There already exists a file with that name " + newPath.getFileName() + " so I won't rename it"); - newFileList.add(oldLinkedFile); + for (LinkedFile file : files) { + if (onlyRelativePaths && Paths.get(file.getLink()).isAbsolute()) { continue; } + LinkedFileHandler fileHandler = new LinkedFileHandler(file, entry, databaseContext, filePreferences); try { - if (!Files.exists(newPath)) { - Files.createDirectories(newPath); + boolean changedFile = fileHandler.renameToSuggestedName(); + if (changedFile) { + changed = true; } - } catch (IOException e) { - LOGGER.error("Could not create necessary target directories for renaming", e); - } - - boolean renameSuccessful = FileUtil.renameFileWithException(Paths.get(expandedOldFilePath), newPath, true); - if (renameSuccessful) { - changed = true; - - // Change the path for this entry - String description = oldLinkedFile.getDescription(); - String type = oldLinkedFile.getFileType(); - - // We use the file directory (if none is set - then bib file) to create relative file links. - // The .get() is legal without check because the method will always return a value. - Path settingsDir = databaseContext.getFirstExistingFileDir(fileDirectoryPreferences).get(); - if (settingsDir.getRoot().equals(newPath.getRoot())) { - newFileList.add(new LinkedFile(description, settingsDir.relativize(newPath).toString(), type)); - } else { - newFileList.add(new LinkedFile(description, newPath.toString(), type)); - } - } else { - unsuccessfulRenames++; + } catch (IOException exception) { + LOGGER.error("Error while renaming file {}", file.getLink(), exception); } } + if (changed) { - Optional change = entry.setFiles(newFileList); - //we put an undo of the field content here - //the file is not being renamed back, which leads to inconsistencies - //if we put a null undo object here, the change by "doMakePathsRelative" would overwrite the field value nevertheless. - return change.map(Collections::singletonList).orElseGet(Collections::emptyList); + Optional changes = entry.setFiles(files); + return OptionalUtil.toList(changes); } - return Collections.emptyList(); - } - - public String getTargetFileName(LinkedFile flEntry, BibEntry entry) { - String realOldFilename = flEntry.getLink(); - - String targetFileName = FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, fileNamePattern).trim() - + '.' - + FileHelper.getFileExtension(realOldFilename).orElse("pdf"); - - // Only create valid file names - return FileUtil.getValidFileName(targetFileName); - } - public int getUnsuccessfulRenames() { - return unsuccessfulRenames; - } - - /** - * Check to see if a file already exists in the target directory. Search is not case sensitive. - * @param flEntry - * @param entry - * @return First identified path that matches an existing file. This name can be used in subsequent calls to override the existing file. - */ - public Optional findExistingFile(LinkedFile flEntry, BibEntry entry) { - String targetFileName = getTargetFileName(flEntry, entry); - // The .get() is legal without check because the method will always return a value. - Path targetFilePath = flEntry.findIn(databaseContext, fileDirectoryPreferences) - .get().getParent().resolve(targetFileName); - Path oldFilePath = flEntry.findIn(databaseContext, fileDirectoryPreferences).get(); - //Check if file already exists in directory with different case. - //This is necessary because other entries may have such a file. - Optional matchedByDiffCase = Optional.empty(); - try (Stream stream = Files.list(oldFilePath.getParent())) { - matchedByDiffCase = stream.filter(name -> name.toString().equalsIgnoreCase(targetFilePath.toString())) - .findFirst(); - } catch (IOException e) { - LOGGER.error("Could not get the list of files in target directory", e); - } - return matchedByDiffCase; + return Collections.emptyList(); } } diff --git a/src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java b/src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java new file mode 100644 index 00000000000..ac38e27c560 --- /dev/null +++ b/src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java @@ -0,0 +1,144 @@ +package org.jabref.logic.externalfiles; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import org.jabref.logic.util.io.FileUtil; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.LinkedFile; +import org.jabref.model.metadata.FilePreferences; +import org.jabref.model.util.FileHelper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LinkedFileHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(LinkedFileHandler.class); + + private final BibDatabaseContext databaseContext; + private final FilePreferences filePreferences; + private final BibEntry entry; + + private final LinkedFile fileEntry; + + public LinkedFileHandler(LinkedFile fileEntry, BibEntry entry, BibDatabaseContext databaseContext, FilePreferences filePreferences) { + this.fileEntry = fileEntry; + this.entry = entry; + this.databaseContext = Objects.requireNonNull(databaseContext); + this.filePreferences = Objects.requireNonNull(filePreferences); + } + + public boolean moveToDefaultDirectory() throws IOException { + Optional targetDirectory = databaseContext.getFirstExistingFileDir(filePreferences); + if (!targetDirectory.isPresent()) { + return false; + } + + Optional oldFile = fileEntry.findIn(databaseContext, filePreferences); + if (!oldFile.isPresent()) { + // Could not find file + return false; + } + + String targetDirName = ""; + if (!filePreferences.getFileDirPattern().isEmpty()) { + targetDirName = FileUtil.createDirNameFromPattern(databaseContext.getDatabase(), entry, filePreferences.getFileDirPattern()); + } + + Path targetPath = targetDirectory.get().resolve(targetDirName).resolve(oldFile.get().getFileName()); + if (Files.exists(targetPath)) { + // We do not overwrite already existing files + LOGGER.debug("The file {} would have been moved to {}. However, there exists already a file with that name so we do nothing.", oldFile.get(), targetPath); + return false; + } else { + // Make sure sub-directories exist + Files.createDirectories(targetPath.getParent()); + } + + // Move + Files.move(oldFile.get(), targetPath); + + // Update path + fileEntry.setLink(relativize(targetPath)); + return true; + } + + public boolean renameToSuggestedName() throws IOException { + Optional oldFile = fileEntry.findIn(databaseContext, filePreferences); + if (!oldFile.isPresent()) { + // Could not find file + return false; + } + + String targetFileName = getSuggestedFileName(); + Path newPath = oldFile.get().resolveSibling(targetFileName); + + String expandedOldFilePath = oldFile.get().toString(); + boolean pathsDifferOnlyByCase = newPath.toString().equalsIgnoreCase(expandedOldFilePath) + && !newPath.toString().equals(expandedOldFilePath); + + if (Files.exists(newPath) && !pathsDifferOnlyByCase) { + // We do not overwrite files + // Since Files.exists is sometimes not case-sensitive, the check pathsDifferOnlyByCase ensures that we + // nonetheless rename files to a new name which just differs by case. + LOGGER.debug("The file {} would have been moved to {}. However, there exists already a file with that name so we do nothing.", oldFile.get(), newPath); + return false; + } else { + Files.createDirectories(newPath.getParent()); + } + + // Rename + Files.move(oldFile.get(), newPath); + + // Update path + fileEntry.setLink(relativize(newPath)); + + return true; + } + + private String relativize(Path path) { + List fileDirectories = databaseContext.getFileDirectoriesAsPaths(filePreferences); + return FileUtil.relativize(path, fileDirectories).toString(); + } + + public String getSuggestedFileName() { + String oldFileName = fileEntry.getLink(); + + String targetFileName = FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, filePreferences.getFileNamePattern()).trim() + + '.' + + FileHelper.getFileExtension(oldFileName).orElse(fileEntry.getFileType()); + + // Only create valid file names + return FileUtil.getValidFileName(targetFileName); + } + + /** + * Check to see if a file already exists in the target directory. Search is not case sensitive. + * + * @return First identified path that matches an existing file. This name can be used in subsequent calls to + * override the existing file. + */ + public Optional findExistingFile(LinkedFile flEntry, BibEntry entry) { + String targetFileName = getSuggestedFileName(); + // The .get() is legal without check because the method will always return a value. + Path targetFilePath = flEntry.findIn(databaseContext, filePreferences) + .get().getParent().resolve(targetFileName); + Path oldFilePath = flEntry.findIn(databaseContext, filePreferences).get(); + //Check if file already exists in directory with different case. + //This is necessary because other entries may have such a file. + Optional matchedByDiffCase = Optional.empty(); + try (Stream stream = Files.list(oldFilePath.getParent())) { + matchedByDiffCase = stream.filter(name -> name.toString().equalsIgnoreCase(targetFilePath.toString())) + .findFirst(); + } catch (IOException e) { + LOGGER.error("Could not get the list of files in target directory", e); + } + return matchedByDiffCase; + } +} diff --git a/src/main/java/org/jabref/logic/integrity/FieldCheckers.java b/src/main/java/org/jabref/logic/integrity/FieldCheckers.java index 79cca452b11..df473a94604 100644 --- a/src/main/java/org/jabref/logic/integrity/FieldCheckers.java +++ b/src/main/java/org/jabref/logic/integrity/FieldCheckers.java @@ -8,7 +8,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.FieldName; import org.jabref.model.entry.InternalBibtexFields; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; @@ -17,11 +17,11 @@ public class FieldCheckers { private Multimap fieldChecker; - public FieldCheckers(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences, JournalAbbreviationRepository abbreviationRepository, boolean enforceLegalKey) { - fieldChecker = getAllMap(databaseContext, fileDirectoryPreferences, abbreviationRepository, enforceLegalKey); + public FieldCheckers(BibDatabaseContext databaseContext, FilePreferences filePreferences, JournalAbbreviationRepository abbreviationRepository, boolean enforceLegalKey) { + fieldChecker = getAllMap(databaseContext, filePreferences, abbreviationRepository, enforceLegalKey); } - private static Multimap getAllMap(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences, JournalAbbreviationRepository abbreviationRepository, boolean enforceLegalKey) { + private static Multimap getAllMap(BibDatabaseContext databaseContext, FilePreferences filePreferences, JournalAbbreviationRepository abbreviationRepository, boolean enforceLegalKey) { ArrayListMultimap fieldCheckers = ArrayListMultimap.create(50, 10); for (String field : InternalBibtexFields.getJournalNameFields()) { @@ -38,7 +38,7 @@ private static Multimap getAllMap(BibDatabaseContext datab fieldCheckers.put(FieldName.TITLE, new TitleChecker(databaseContext)); fieldCheckers.put(FieldName.DOI, new DOIValidityChecker()); fieldCheckers.put(FieldName.EDITION, new EditionChecker(databaseContext)); - fieldCheckers.put(FieldName.FILE, new FileChecker(databaseContext, fileDirectoryPreferences)); + fieldCheckers.put(FieldName.FILE, new FileChecker(databaseContext, filePreferences)); fieldCheckers.put(FieldName.HOWPUBLISHED, new HowPublishedChecker(databaseContext)); fieldCheckers.put(FieldName.ISBN, new ISBNChecker()); fieldCheckers.put(FieldName.ISSN, new ISSNChecker()); diff --git a/src/main/java/org/jabref/logic/integrity/FileChecker.java b/src/main/java/org/jabref/logic/integrity/FileChecker.java index f631230922b..b70cb834dc0 100644 --- a/src/main/java/org/jabref/logic/integrity/FileChecker.java +++ b/src/main/java/org/jabref/logic/integrity/FileChecker.java @@ -10,18 +10,17 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.FileFieldParser; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.strings.StringUtil; public class FileChecker implements ValueChecker { private final BibDatabaseContext context; - private final FileDirectoryPreferences fileDirectoryPreferences; + private final FilePreferences filePreferences; - - public FileChecker(BibDatabaseContext context, FileDirectoryPreferences fileDirectoryPreferences) { + public FileChecker(BibDatabaseContext context, FilePreferences filePreferences) { this.context = context; - this.fileDirectoryPreferences = fileDirectoryPreferences; + this.filePreferences = filePreferences; } @Override @@ -35,7 +34,7 @@ public Optional checkValue(String value) { .collect(Collectors.toList()); for (LinkedFile file : linkedFiles) { - Optional linkedFile = file.findIn(context, fileDirectoryPreferences); + Optional linkedFile = file.findIn(context, filePreferences); if ((!linkedFile.isPresent()) || !Files.exists(linkedFile.get())) { return Optional.of(Localization.lang("link should refer to a correct file path")); } diff --git a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java index f23573afd4d..7932c842ae1 100644 --- a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java +++ b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java @@ -9,23 +9,23 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FieldName; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; public class IntegrityCheck { private final BibDatabaseContext bibDatabaseContext; - private final FileDirectoryPreferences fileDirectoryPreferences; + private final FilePreferences filePreferences; private final BibtexKeyPatternPreferences bibtexKeyPatternPreferences; private final JournalAbbreviationRepository journalAbbreviationRepository; private final boolean enforceLegalKey; public IntegrityCheck(BibDatabaseContext bibDatabaseContext, - FileDirectoryPreferences fileDirectoryPreferences, + FilePreferences filePreferences, BibtexKeyPatternPreferences bibtexKeyPatternPreferences, JournalAbbreviationRepository journalAbbreviationRepository, boolean enforceLegalKey) { this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext); - this.fileDirectoryPreferences = Objects.requireNonNull(fileDirectoryPreferences); + this.filePreferences = Objects.requireNonNull(filePreferences); this.bibtexKeyPatternPreferences = Objects.requireNonNull(bibtexKeyPatternPreferences); this.journalAbbreviationRepository = Objects.requireNonNull(journalAbbreviationRepository); this.enforceLegalKey = enforceLegalKey; @@ -48,7 +48,7 @@ private List checkBibtexEntry(BibEntry entry) { return result; } - FieldCheckers fieldCheckers = new FieldCheckers(bibDatabaseContext, fileDirectoryPreferences, journalAbbreviationRepository, enforceLegalKey); + FieldCheckers fieldCheckers = new FieldCheckers(bibDatabaseContext, filePreferences, journalAbbreviationRepository, enforceLegalKey); for (FieldChecker checker : fieldCheckers.getAll()) { result.addAll(checker.check(entry)); } diff --git a/src/main/java/org/jabref/logic/pdf/EntryAnnotationImporter.java b/src/main/java/org/jabref/logic/pdf/EntryAnnotationImporter.java index 8953c139e11..12bf844cf1f 100644 --- a/src/main/java/org/jabref/logic/pdf/EntryAnnotationImporter.java +++ b/src/main/java/org/jabref/logic/pdf/EntryAnnotationImporter.java @@ -9,7 +9,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.pdf.FileAnnotation; @@ -44,14 +44,14 @@ private List getFilteredFileList() { * @param databaseContext The context is needed for the importer. * @return Map from each PDF to a list of file annotations */ - public Map> importAnnotationsFromFiles(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences) { + public Map> importAnnotationsFromFiles(BibDatabaseContext databaseContext, FilePreferences filePreferences) { Map> annotations = new HashMap<>(); AnnotationImporter importer = new PdfAnnotationImporter(); //import annotationsOfFiles if the selected files are valid which is checked in getFilteredFileList() for (LinkedFile linkedFile : this.getFilteredFileList()) { - linkedFile.findIn(databaseContext, fileDirectoryPreferences) - .ifPresent(file -> annotations.put(file, importer.importAnnotations(file))); + linkedFile.findIn(databaseContext, filePreferences) + .ifPresent(file -> annotations.put(file, importer.importAnnotations(file))); } return annotations; } diff --git a/src/main/java/org/jabref/logic/pdf/FileAnnotationCache.java b/src/main/java/org/jabref/logic/pdf/FileAnnotationCache.java index fd59cfde876..22a2e7cb872 100644 --- a/src/main/java/org/jabref/logic/pdf/FileAnnotationCache.java +++ b/src/main/java/org/jabref/logic/pdf/FileAnnotationCache.java @@ -6,7 +6,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.pdf.FileAnnotation; import com.google.common.cache.CacheBuilder; @@ -32,11 +32,11 @@ public FileAnnotationCache() { } - public FileAnnotationCache(BibDatabaseContext context, FileDirectoryPreferences fileDirectoryPreferences) { + public FileAnnotationCache(BibDatabaseContext context, FilePreferences filePreferences) { annotationCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).build(new CacheLoader>>() { @Override public Map> load(BibEntry entry) throws Exception { - return new EntryAnnotationImporter(entry).importAnnotationsFromFiles(context, fileDirectoryPreferences); + return new EntryAnnotationImporter(entry).importAnnotationsFromFiles(context, filePreferences); } }); } diff --git a/src/main/java/org/jabref/logic/util/io/DatabaseFileLookup.java b/src/main/java/org/jabref/logic/util/io/DatabaseFileLookup.java index d8f17f80fef..7dff7fe0695 100644 --- a/src/main/java/org/jabref/logic/util/io/DatabaseFileLookup.java +++ b/src/main/java/org/jabref/logic/util/io/DatabaseFileLookup.java @@ -13,7 +13,7 @@ import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; /** * Search class for files.
@@ -30,10 +30,10 @@ public class DatabaseFileLookup { /** * Creates an instance by passing a {@link BibDatabase} which will be used for the searches. */ - public DatabaseFileLookup(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences) { + public DatabaseFileLookup(BibDatabaseContext databaseContext, FilePreferences filePreferences) { Objects.requireNonNull(databaseContext); - possibleFilePaths = Optional.ofNullable(databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences)) - .orElse(new ArrayList<>()); + possibleFilePaths = Optional.ofNullable(databaseContext.getFileDirectoriesAsPaths(filePreferences)) + .orElse(new ArrayList<>()); for (BibEntry entry : databaseContext.getDatabase().getEntries()) { fileCache.addAll(parseFileField(entry)); diff --git a/src/main/java/org/jabref/logic/util/io/FileUtil.java b/src/main/java/org/jabref/logic/util/io/FileUtil.java index 8a759739528..10a28fb45cd 100644 --- a/src/main/java/org/jabref/logic/util/io/FileUtil.java +++ b/src/main/java/org/jabref/logic/util/io/FileUtil.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.CopyOption; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -188,7 +189,7 @@ public static boolean renameFile(Path fromFile, Path toFile) { * @param toFile The target fileName * @param replaceExisting Wether to replace existing files or not * @return True if the rename was successful, false if an exception occurred - * @deprecated Use {@link #renameFileWithException(Path, Path, boolean)} instead and handle exception properly + * @deprecated Use {@link Files#move(Path, Path, CopyOption...)} instead and handle exception properly */ @Deprecated public static boolean renameFile(Path fromFile, Path toFile, boolean replaceExisting) { @@ -200,6 +201,10 @@ public static boolean renameFile(Path fromFile, Path toFile, boolean replaceExis } } + /** + * @deprecated Directly use {@link Files#move(Path, Path, CopyOption...)} + */ + @Deprecated public static boolean renameFileWithException(Path fromFile, Path toFile, boolean replaceExisting) throws IOException { if (replaceExisting) { return Files.move(fromFile, fromFile.resolveSibling(toFile), @@ -213,19 +218,19 @@ public static boolean renameFileWithException(Path fromFile, Path toFile, boolea * Converts an absolute file to a relative one, if possible. Returns the parameter file itself if no shortening is * possible. *

- * This method works correctly only if dirs are sorted decent in their length i.e. /home/user/literature/important before /home/user/literature. + * This method works correctly only if directories are sorted decent in their length i.e. /home/user/literature/important before /home/user/literature. * * @param file the file to be shortened - * @param dirs directories to check + * @param directories directories to check */ - public static Path shortenFileName(Path file, List dirs) { + public static Path relativize(Path file, List directories) { if (!file.isAbsolute()) { return file; } - for (Path dir : dirs) { - if (file.startsWith(dir)) { - return dir.relativize(file); + for (Path directory : directories) { + if (file.startsWith(directory)) { + return directory.relativize(file); } } return file; diff --git a/src/main/java/org/jabref/migrations/FileLinksUpgradeWarning.java b/src/main/java/org/jabref/migrations/FileLinksUpgradeWarning.java index 8668bc30843..eed9497457e 100644 --- a/src/main/java/org/jabref/migrations/FileLinksUpgradeWarning.java +++ b/src/main/java/org/jabref/migrations/FileLinksUpgradeWarning.java @@ -25,7 +25,7 @@ import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FieldName; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.preferences.JabRefPreferences; import com.jgoodies.forms.builder.FormBuilder; @@ -87,9 +87,9 @@ public boolean isActionNecessary(ParserResult pr) { // Only offer to upgrade links if the pdf/ps fields are used: offerChangeDatabase = linksFound(pr.getDatabase(), FileLinksUpgradeWarning.FIELDS_TO_LOOK_FOR); // If the "file" directory is not set, offer to migrate pdf/ps dir: - offerSetFileDir = !Globals.prefs.hasKey(FieldName.FILE + FileDirectoryPreferences.DIR_SUFFIX) - && (Globals.prefs.hasKey(FieldName.PDF + FileDirectoryPreferences.DIR_SUFFIX) - || Globals.prefs.hasKey(FieldName.PS + FileDirectoryPreferences.DIR_SUFFIX)); + offerSetFileDir = !Globals.prefs.hasKey(FieldName.FILE + FilePreferences.DIR_SUFFIX) + && (Globals.prefs.hasKey(FieldName.PDF + FilePreferences.DIR_SUFFIX) + || Globals.prefs.hasKey(FieldName.PS + FilePreferences.DIR_SUFFIX)); // First check if this warning is disabled: return Globals.prefs.getBoolean(JabRefPreferences.SHOW_FILE_LINKS_UPGRADE_WARNING) @@ -141,10 +141,10 @@ public void performAction(BasePanel panel, ParserResult parserResult) { formBuilder.add(changeDatabase).xy(1, row); } if (offerSetFileDir) { - if (Globals.prefs.hasKey(FieldName.PDF + FileDirectoryPreferences.DIR_SUFFIX)) { - fileDir.setText(Globals.prefs.get(FieldName.PDF + FileDirectoryPreferences.DIR_SUFFIX)); + if (Globals.prefs.hasKey(FieldName.PDF + FilePreferences.DIR_SUFFIX)) { + fileDir.setText(Globals.prefs.get(FieldName.PDF + FilePreferences.DIR_SUFFIX)); } else { - fileDir.setText(Globals.prefs.get(FieldName.PS + FileDirectoryPreferences.DIR_SUFFIX)); + fileDir.setText(Globals.prefs.get(FieldName.PS + FilePreferences.DIR_SUFFIX)); } JPanel builderPanel = new JPanel(); builderPanel.add(setFileDir); @@ -220,7 +220,7 @@ private void makeChanges(BasePanel panel, ParserResult pr, boolean upgradePrefs, } if (fileDir != null) { - Globals.prefs.put(FieldName.FILE + FileDirectoryPreferences.DIR_SUFFIX, fileDir); + Globals.prefs.put(FieldName.FILE + FilePreferences.DIR_SUFFIX, fileDir); } if (upgradePrefs) { diff --git a/src/main/java/org/jabref/model/database/BibDatabaseContext.java b/src/main/java/org/jabref/model/database/BibDatabaseContext.java index ece8531db46..adabb5805ed 100644 --- a/src/main/java/org/jabref/model/database/BibDatabaseContext.java +++ b/src/main/java/org/jabref/model/database/BibDatabaseContext.java @@ -16,7 +16,7 @@ import org.jabref.model.database.shared.DatabaseSynchronizer; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FieldName; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.metadata.MetaData; /** @@ -151,7 +151,7 @@ public boolean isBiblatexMode() { return getMode() == BibDatabaseMode.BIBLATEX; } - public List getFileDirectoriesAsPaths(FileDirectoryPreferences preferences) { + public List getFileDirectoriesAsPaths(FilePreferences preferences) { // Filter for empty string, as this would be expanded to the jar-directory with Paths.get() return getFileDirectories(preferences).stream() .filter(s -> !s.isEmpty()) @@ -162,20 +162,20 @@ public List getFileDirectoriesAsPaths(FileDirectoryPreferences preferences } /** - * @deprecated use {@link #getFileDirectoriesAsPaths(FileDirectoryPreferences)} instead + * @deprecated use {@link #getFileDirectoriesAsPaths(FilePreferences)} instead */ @Deprecated - public List getFileDirectories(FileDirectoryPreferences preferences) { + public List getFileDirectories(FilePreferences preferences) { return getFileDirectories(FieldName.FILE, preferences); } /** - * Returns the first existing file directory from {@link #getFileDirectories(FileDirectoryPreferences)} + * Returns the first existing file directory from {@link #getFileDirectories(FilePreferences)} * - * @param preferences The FileDirectoryPreferences + * @param preferences The FilePreferences * @return Optional of Path */ - public Optional getFirstExistingFileDir(FileDirectoryPreferences preferences) { + public Optional getFirstExistingFileDir(FilePreferences preferences) { return getFileDirectoriesAsPaths(preferences).stream().filter(Files::exists).findFirst(); } @@ -195,7 +195,7 @@ public Optional getFirstExistingFileDir(FileDirectoryPreferences preferenc * @param preferences The fileDirectory preferences * @return The default directory for this field type. */ - public List getFileDirectories(String fieldName, FileDirectoryPreferences preferences) { + public List getFileDirectories(String fieldName, FilePreferences preferences) { List fileDirs = new ArrayList<>(); // 1. metadata user-specific directory diff --git a/src/main/java/org/jabref/model/entry/LinkedFile.java b/src/main/java/org/jabref/model/entry/LinkedFile.java index 93b99e347af..63d0dfb2cea 100644 --- a/src/main/java/org/jabref/model/entry/LinkedFile.java +++ b/src/main/java/org/jabref/model/entry/LinkedFile.java @@ -5,6 +5,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.URL; +import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; @@ -17,7 +18,7 @@ import javafx.beans.property.StringProperty; import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.util.FileHelper; /** @@ -154,16 +155,29 @@ public boolean isOnlineLink() { return isOnlineLink(link.get()); } - public Optional findIn(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences) { - List dirs = databaseContext.getFileDirectoriesAsPaths(fileDirectoryPreferences); + public Optional findIn(BibDatabaseContext databaseContext, FilePreferences filePreferences) { + List dirs = databaseContext.getFileDirectoriesAsPaths(filePreferences); return findIn(dirs); } + /** + * Tries to find the file in the given directories and returns the path to the file (if found). Returns an empty + * optional if the file cannot be found. + */ public Optional findIn(List directories) { try { + if (link.get().isEmpty()) { + // We do not want to match empty paths (which could be any file or none ?!) + return Optional.empty(); + } + Path file = Paths.get(link.get()); if (file.isAbsolute() || directories.isEmpty()) { - return Optional.of(file); + if (Files.exists(file)) { + return Optional.of(file); + } else { + return Optional.empty(); + } } else { return FileHelper.expandFilenameAsPath(link.get(), directories); } @@ -171,5 +185,4 @@ public Optional findIn(List directories) { return Optional.empty(); } } - } diff --git a/src/main/java/org/jabref/model/metadata/FileDirectoryPreferences.java b/src/main/java/org/jabref/model/metadata/FilePreferences.java similarity index 66% rename from src/main/java/org/jabref/model/metadata/FileDirectoryPreferences.java rename to src/main/java/org/jabref/model/metadata/FilePreferences.java index b24a699da8d..47b3874b195 100644 --- a/src/main/java/org/jabref/model/metadata/FileDirectoryPreferences.java +++ b/src/main/java/org/jabref/model/metadata/FilePreferences.java @@ -8,18 +8,25 @@ import org.jabref.model.entry.FieldName; -public class FileDirectoryPreferences { +public class FilePreferences { public static final String DIR_SUFFIX = "Directory"; private final String user; private final Map fieldFileDirectories; private final boolean bibLocationAsPrimary; + private final String fileNamePattern; + private final String fileDirPattern; - - public FileDirectoryPreferences(String user, Map fieldFileDirectories, boolean bibLocationAsPrimary) { + public FilePreferences(String user, + Map fieldFileDirectories, + boolean bibLocationAsPrimary, + String fileNamePattern, + String fileDirPattern) { this.user = user; this.fieldFileDirectories = fieldFileDirectories; this.bibLocationAsPrimary = bibLocationAsPrimary; + this.fileNamePattern = fileNamePattern; + this.fileDirPattern = fileDirPattern; } public String getUser() { @@ -47,4 +54,12 @@ public Optional getFileDirectory() { public boolean isBibLocationAsPrimary() { return bibLocationAsPrimary; } + + public String getFileNamePattern() { + return fileNamePattern; + } + + public String getFileDirPattern() { + return fileDirPattern; + } } diff --git a/src/main/java/org/jabref/model/metadata/MetaData.java b/src/main/java/org/jabref/model/metadata/MetaData.java index dbd662681c1..94b105eadac 100644 --- a/src/main/java/org/jabref/model/metadata/MetaData.java +++ b/src/main/java/org/jabref/model/metadata/MetaData.java @@ -31,7 +31,7 @@ public class MetaData { public static final String DATABASE_TYPE = "databaseType"; public static final String GROUPSTREE = "grouping"; public static final String GROUPSTREE_LEGACY = "groupstree"; - public static final String FILE_DIRECTORY = FieldName.FILE + FileDirectoryPreferences.DIR_SUFFIX; + public static final String FILE_DIRECTORY = FieldName.FILE + FilePreferences.DIR_SUFFIX; public static final String PROTECTED_FLAG_META = "protectedFlag"; public static final String SELECTOR_META_PREFIX = "selector_"; diff --git a/src/main/java/org/jabref/model/util/FileHelper.java b/src/main/java/org/jabref/model/util/FileHelper.java index bd75c54ca95..f972d42cc46 100644 --- a/src/main/java/org/jabref/model/util/FileHelper.java +++ b/src/main/java/org/jabref/model/util/FileHelper.java @@ -10,7 +10,7 @@ import java.util.Optional; import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; public class FileHelper { @@ -53,12 +53,12 @@ public static Optional getFileExtension(String fileName) { * @param name The filename, may also be a relative path to the file */ public static Optional expandFilename(final BibDatabaseContext databaseContext, String name, - FileDirectoryPreferences fileDirectoryPreferences) { + FilePreferences filePreferences) { Optional extension = getFileExtension(name); // Find the default directory for this field type, if any: - List directories = databaseContext.getFileDirectories(extension.orElse(null), fileDirectoryPreferences); + List directories = databaseContext.getFileDirectories(extension.orElse(null), filePreferences); // Include the standard "file" directory: - List fileDir = databaseContext.getFileDirectories(fileDirectoryPreferences); + List fileDir = databaseContext.getFileDirectories(filePreferences); List searchDirectories = new ArrayList<>(); for (String dir : directories) { diff --git a/src/main/java/org/jabref/pdfimport/PdfImporter.java b/src/main/java/org/jabref/pdfimport/PdfImporter.java index 7f1298cfc44..f0c28b563eb 100644 --- a/src/main/java/org/jabref/pdfimport/PdfImporter.java +++ b/src/main/java/org/jabref/pdfimport/PdfImporter.java @@ -164,10 +164,10 @@ private void doXMPImport(String fileName, List res) { Path toLink = Paths.get(fileName); // Get a list of file directories: List dirsS = panel.getBibDatabaseContext() - .getFileDirectoriesAsPaths(Globals.prefs.getFileDirectoryPreferences()); + .getFileDirectoriesAsPaths(Globals.prefs.getFilePreferences()); - tm.addEntry(0, new FileListEntry("", FileUtil.shortenFileName(toLink, dirsS).toString(), - ExternalFileTypes.getInstance().getExternalFileTypeByName("PDF"))); + tm.addEntry(0, new FileListEntry("", FileUtil.relativize(toLink, dirsS).toString(), + ExternalFileTypes.getInstance().getExternalFileTypeByName("PDF"))); entry.setField(FieldName.FILE, tm.getStringRepresentation()); res.add(entry); } diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index d398ff19978..b406dcf8950 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -91,7 +91,7 @@ import org.jabref.model.entry.FieldName; import org.jabref.model.entry.InternalBibtexFields; import org.jabref.model.entry.specialfields.SpecialField; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.metadata.SaveOrderConfig; import org.jabref.model.strings.StringUtil; @@ -1392,13 +1392,16 @@ public void storeFileHistory(FileHistory history) { } @Override - public FileDirectoryPreferences getFileDirectoryPreferences() { - List fields = Arrays.asList(FieldName.FILE, FieldName.PDF, FieldName.PS); - Map fieldDirectories = new HashMap<>(); - fields.stream().forEach( - fieldName -> fieldDirectories.put(fieldName, get(fieldName + FileDirectoryPreferences.DIR_SUFFIX))); - return new FileDirectoryPreferences(getUser(), fieldDirectories, - getBoolean(JabRefPreferences.BIB_LOC_AS_PRIMARY_DIR)); + public FilePreferences getFilePreferences() { + Map fieldDirectories = Stream.of(FieldName.FILE, FieldName.PDF, FieldName.PS) + .collect(Collectors.toMap(field -> field, field -> get(field + FilePreferences.DIR_SUFFIX, ""))); + return new FilePreferences( + getUser(), + fieldDirectories, + getBoolean(JabRefPreferences.BIB_LOC_AS_PRIMARY_DIR), + get(IMPORT_FILENAMEPATTERN), + get(IMPORT_FILEDIRPATTERN) + ); } public UpdateFieldPreferences getUpdateFieldPreferences() { @@ -1526,7 +1529,7 @@ private NameFormatterPreferences getNameFormatterPreferences() { public FileLinkPreferences getFileLinkPreferences() { return new FileLinkPreferences( - Collections.singletonList(get(FieldName.FILE + FileDirectoryPreferences.DIR_SUFFIX)), + Collections.singletonList(get(FieldName.FILE + FilePreferences.DIR_SUFFIX)), fileDirForDatabase); } @@ -1620,8 +1623,9 @@ public JournalAbbreviationPreferences getJournalAbbreviationPreferences() { } public CleanupPreferences getCleanupPreferences(JournalAbbreviationLoader journalAbbreviationLoader) { - return new CleanupPreferences(get(IMPORT_FILENAMEPATTERN), get(IMPORT_FILEDIRPATTERN), - getLayoutFormatterPreferences(journalAbbreviationLoader), getFileDirectoryPreferences()); + return new CleanupPreferences( + getLayoutFormatterPreferences(journalAbbreviationLoader), + getFilePreferences()); } public CleanupPreset getCleanupPreset() { diff --git a/src/main/java/org/jabref/preferences/PreferencesService.java b/src/main/java/org/jabref/preferences/PreferencesService.java index 1245e404ba7..7e869e518d5 100644 --- a/src/main/java/org/jabref/preferences/PreferencesService.java +++ b/src/main/java/org/jabref/preferences/PreferencesService.java @@ -4,7 +4,7 @@ import org.jabref.gui.keyboard.KeyBindingRepository; import org.jabref.logic.journals.JournalAbbreviationPreferences; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; public interface PreferencesService { JournalAbbreviationPreferences getJournalAbbreviationPreferences(); @@ -15,7 +15,7 @@ public interface PreferencesService { void storeJournalAbbreviationPreferences(JournalAbbreviationPreferences abbreviationsPreferences); - FileDirectoryPreferences getFileDirectoryPreferences(); + FilePreferences getFilePreferences(); Path getWorkingDir(); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 58fe003e16b..23785537e34 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1620,7 +1620,6 @@ Print\ entry\ preview=Print entry preview Copy\ title=Copy title Copy\ \\cite{BibTeX\ key}=Copy \\cite{BibTeX key} Copy\ BibTeX\ key\ and\ title=Copy BibTeX key and title -File\ rename\ failed\ for\ %0\ entries.=File rename failed for %0 entries. Merged\ BibTeX\ source\ code=Merged BibTeX source code Invalid\ DOI\:\ '%0'.=Invalid DOI: '%0'. should\ start\ with\ a\ name=should start with a name @@ -2242,4 +2241,4 @@ Keystore\ password\:=Keystore password\: Keystore\:=Keystore\: Password\:=Password\: Remember\ Password=Remember Password -Use\ SSL=Use SSL \ No newline at end of file +Use\ SSL=Use SSL diff --git a/src/test/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtilTest.java b/src/test/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtilTest.java index 6b97ff2965a..c2e2773a31f 100644 --- a/src/test/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtilTest.java +++ b/src/test/java/org/jabref/gui/externalfiles/AutoSetFileLinksUtilTest.java @@ -11,10 +11,10 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junitpioneer.jupiter.TempDirectory; @@ -26,7 +26,7 @@ @ExtendWith(TempDirectory.class) public class AutoSetFileLinksUtilTest { - private final FileDirectoryPreferences fileDirPrefs = mock(FileDirectoryPreferences.class); + private final FilePreferences fileDirPrefs = mock(FilePreferences.class); private final AutoLinkPreferences autoLinkPrefs = new AutoLinkPreferences(false, "", true, ';'); private final BibDatabaseContext databaseContext = mock(BibDatabaseContext.class); private final ExternalFileTypes externalFileTypes = mock(ExternalFileTypes.class); diff --git a/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java b/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java index 3bf8d043435..a2fcce655cf 100644 --- a/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java +++ b/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java @@ -12,13 +12,14 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.preferences.JabRefPreferences; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junitpioneer.jupiter.TempDirectory; +import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -27,7 +28,6 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -35,13 +35,12 @@ class LinkedFileViewModelTest { private Path tempFile; - private final JabRefPreferences preferences = mock(JabRefPreferences.class); + private final JabRefPreferences preferences = mock(JabRefPreferences.class, Answers.RETURNS_DEEP_STUBS); private LinkedFile linkedFile; private BibEntry entry; private BibDatabaseContext databaseContext; private TaskExecutor taskExecutor; private DialogService dialogService; - private final FileDirectoryPreferences fileDirectoryPreferences = mock(FileDirectoryPreferences.class); @BeforeEach void setUp(@TempDirectory.TempDir Path tempFolder) throws Exception { @@ -58,10 +57,10 @@ void setUp(@TempDirectory.TempDir Path tempFolder) throws Exception { void deleteWhenFilePathNotPresentReturnsTrue() { // Making this a spy, so we can inject an empty optional without digging into the implementation linkedFile = spy(new LinkedFile("", "nonexistent file", "")); - doReturn(Optional.empty()).when(linkedFile).findIn(any(BibDatabaseContext.class), any(FileDirectoryPreferences.class)); + doReturn(Optional.empty()).when(linkedFile).findIn(any(BibDatabaseContext.class), any(FilePreferences.class)); LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, taskExecutor, dialogService, preferences); - boolean removed = viewModel.delete(fileDirectoryPreferences); + boolean removed = viewModel.delete(); assertTrue(removed); verifyZeroInteractions(dialogService); // dialog was never shown @@ -79,7 +78,7 @@ void deleteWhenRemoveChosenReturnsTrueButDoesNotDeletesFile() { any(ButtonType.class))).thenAnswer(invocation -> Optional.of(invocation.getArgument(3))); // first vararg - remove button LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, taskExecutor, dialogService, preferences); - boolean removed = viewModel.delete(fileDirectoryPreferences); + boolean removed = viewModel.delete(); assertTrue(removed); assertTrue(Files.exists(tempFile)); @@ -97,14 +96,14 @@ void deleteWhenDeleteChosenReturnsTrueAndDeletesFile() { any(ButtonType.class))).thenAnswer(invocation -> Optional.of(invocation.getArgument(4))); // second vararg - delete button LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, taskExecutor, dialogService, preferences); - boolean removed = viewModel.delete(fileDirectoryPreferences); + boolean removed = viewModel.delete(); assertTrue(removed); assertFalse(Files.exists(tempFile)); } @Test - void deleteWhenDeleteChosenAndFileMissingReturnsFalse() { + void deleteMissingFileReturnsTrue() { linkedFile = new LinkedFile("", "!!nonexistent file!!", ""); when(dialogService.showCustomButtonDialogAndWait( any(AlertType.class), @@ -115,10 +114,9 @@ void deleteWhenDeleteChosenAndFileMissingReturnsFalse() { any(ButtonType.class))).thenAnswer(invocation -> Optional.of(invocation.getArgument(4))); // second vararg - delete button LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, taskExecutor, dialogService, preferences); - boolean removed = viewModel.delete(fileDirectoryPreferences); + boolean removed = viewModel.delete(); - verify(dialogService).showErrorDialogAndWait(anyString(), anyString()); - assertFalse(removed); + assertTrue(removed); } @Test @@ -133,7 +131,7 @@ void deleteWhenDialogCancelledReturnsFalseAndDoesNotRemoveFile() { any(ButtonType.class))).thenAnswer(invocation -> Optional.of(invocation.getArgument(5))); // third vararg - cancel button LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, taskExecutor, dialogService, preferences); - boolean removed = viewModel.delete(fileDirectoryPreferences); + boolean removed = viewModel.delete(); assertFalse(removed); assertTrue(Files.exists(tempFile)); diff --git a/src/test/java/org/jabref/logic/cleanup/CleanupWorkerTest.java b/src/test/java/org/jabref/logic/cleanup/CleanupWorkerTest.java index 4dab917f68c..124e08b2304 100644 --- a/src/test/java/org/jabref/logic/cleanup/CleanupWorkerTest.java +++ b/src/test/java/org/jabref/logic/cleanup/CleanupWorkerTest.java @@ -2,13 +2,13 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Optional; -import java.nio.file.Path; -import java.nio.file.Files; import org.jabref.logic.formatter.bibtexfields.HtmlToLatexFormatter; import org.jabref.logic.formatter.bibtexfields.LatexCleanupFormatter; @@ -29,13 +29,14 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FileFieldWriter; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.metadata.MetaData; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junitpioneer.jupiter.TempDirectory; -import org.junit.jupiter.api.Test; +import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -62,17 +63,12 @@ void setUp(@TempDirectory.TempDir Path bibFolder) throws IOException { Files.createFile(bibFolder.resolve("test.bib")); context.setDatabaseFile(bibFolder.resolve("test.bib").toFile()); - FileDirectoryPreferences fileDirPrefs = mock(FileDirectoryPreferences.class); + FilePreferences fileDirPrefs = mock(FilePreferences.class, Answers.RETURNS_SMART_NULLS); //Biblocation as Primary overwrites all other dirs when(fileDirPrefs.isBibLocationAsPrimary()).thenReturn(true); worker = new CleanupWorker(context, - //empty fileDirPattern for backwards compatibility - new CleanupPreferences("[bibtexkey]", - "", - mock(LayoutFormatterPreferences.class), - fileDirPrefs)); - + new CleanupPreferences(mock(LayoutFormatterPreferences.class), fileDirPrefs)); } @Test diff --git a/src/test/java/org/jabref/logic/cleanup/ISSNCleanupTest.java b/src/test/java/org/jabref/logic/cleanup/ISSNCleanupTest.java index c4827f7eef0..fbe92bc4a65 100644 --- a/src/test/java/org/jabref/logic/cleanup/ISSNCleanupTest.java +++ b/src/test/java/org/jabref/logic/cleanup/ISSNCleanupTest.java @@ -5,7 +5,7 @@ import org.jabref.logic.layout.LayoutFormatterPreferences; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -20,8 +20,7 @@ public class ISSNCleanupTest { @BeforeEach public void setUp() { worker = new CleanupWorker(mock(BibDatabaseContext.class), - new CleanupPreferences("", "", mock(LayoutFormatterPreferences.class), - mock(FileDirectoryPreferences.class))); + new CleanupPreferences(mock(LayoutFormatterPreferences.class), mock(FilePreferences.class))); } @Test diff --git a/src/test/java/org/jabref/logic/cleanup/MoveFilesCleanupTest.java b/src/test/java/org/jabref/logic/cleanup/MoveFilesCleanupTest.java index 1d54e86a199..9e9d21bfbd0 100644 --- a/src/test/java/org/jabref/logic/cleanup/MoveFilesCleanupTest.java +++ b/src/test/java/org/jabref/logic/cleanup/MoveFilesCleanupTest.java @@ -1,6 +1,5 @@ package org.jabref.logic.cleanup; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -13,11 +12,11 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FileFieldWriter; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.metadata.MetaData; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junitpioneer.jupiter.TempDirectory; @@ -30,155 +29,96 @@ @ExtendWith(TempDirectory.class) class MoveFilesCleanupTest { - private File pdfFolder; - private BibDatabaseContext databaseContext; + private Path defaultFileFolder; + private Path fileBefore; private MoveFilesCleanup cleanup; private BibEntry entry; - private FileDirectoryPreferences fileDirPrefs; + private FilePreferences filePreferences; @BeforeEach void setUp(@TempDirectory.TempDir Path bibFolder) throws IOException { + // The folder where the files should be moved to + defaultFileFolder = bibFolder.resolve("pdf"); + Files.createDirectory(defaultFileFolder); + + // The folder where the files are located originally + Path fileFolder = bibFolder.resolve("files"); + Files.createDirectory(fileFolder); + fileBefore = fileFolder.resolve("test.pdf"); + Files.createFile(fileBefore); + MetaData metaData = new MetaData(); - Path path = bibFolder.resolve("ARandomlyNamedFolder"); - Files.createDirectory(path); - pdfFolder = path.toFile(); - metaData.setDefaultFileDirectory(pdfFolder.getAbsolutePath()); - databaseContext = new BibDatabaseContext(new BibDatabase(), metaData, new Defaults()); - Files.createFile(path.resolve("test.bib")); - databaseContext.setDatabaseFile(path.resolve("test.bib").toFile()); + metaData.setDefaultFileDirectory(defaultFileFolder.toAbsolutePath().toString()); + BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(), metaData, new Defaults()); + Files.createFile(bibFolder.resolve("test.bib")); + databaseContext.setDatabaseFile(bibFolder.resolve("test.bib")); + entry = new BibEntry(); entry.setCiteKey("Toot"); entry.setField("title", "test title"); + entry.setField("year", "1989"); + LinkedFile fileField = new LinkedFile("", fileBefore.toAbsolutePath().toString(), ""); + entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - fileDirPrefs = mock(FileDirectoryPreferences.class); - when(fileDirPrefs.isBibLocationAsPrimary()).thenReturn(false); //Biblocation as Primary overwrites all other dirs, therefore we set it to false here + filePreferences = mock(FilePreferences.class); + when(filePreferences.isBibLocationAsPrimary()).thenReturn(false); //Biblocation as Primary overwrites all other dirs, therefore we set it to false here + cleanup = new MoveFilesCleanup(databaseContext, filePreferences); } @Test - void movesFileFromSubfolder(@TempDirectory.TempDir Path bibFolder) throws IOException { - Path path = bibFolder.resolve("AnotherRandomlyNamedFolder"); - Files.createDirectory(path); - File fileBefore = path.resolve("test.pdf").toFile(); - assertTrue(fileBefore.createNewFile()); - assertTrue(fileBefore.exists()); - - LinkedFile fileField = new LinkedFile("", fileBefore.getAbsolutePath(), ""); - entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - cleanup = new MoveFilesCleanup(databaseContext, "", fileDirPrefs); - + void movesFile() throws Exception { + when(filePreferences.getFileDirPattern()).thenReturn(""); cleanup.cleanup(entry); - assertFalse(fileBefore.exists()); - File fileAfter = pdfFolder.toPath().resolve("test.pdf").toFile(); - assertTrue(fileAfter.exists()); - - assertEquals(Optional.of(FileFieldWriter.getStringRepresentation(new LinkedFile("", fileAfter.getName(), ""))), + Path fileAfter = defaultFileFolder.resolve("test.pdf"); + assertEquals( + Optional.of(FileFieldWriter.getStringRepresentation(new LinkedFile("", "test.pdf", ""))), entry.getField("file")); + assertFalse(Files.exists(fileBefore)); + assertTrue(Files.exists(fileAfter)); } @Test - void movesFileFromSubfolderMultiple(@TempDirectory.TempDir Path bibFolder) throws IOException { - Path path = bibFolder.resolve("AnotherRandomlyNamedFolder"); - Files.createDirectory(path); - File fileBefore = path.resolve("test.pdf").toFile(); - assertTrue(fileBefore.createNewFile()); - assertTrue(fileBefore.exists()); - - LinkedFile fileField = new LinkedFile("", fileBefore.getAbsolutePath(), ""); + void movesFileWithMulitpleLinked() throws Exception { + LinkedFile fileField = new LinkedFile("", fileBefore.toAbsolutePath().toString(), ""); entry.setField("file", FileFieldWriter.getStringRepresentation( Arrays.asList(new LinkedFile("", "", ""), fileField, new LinkedFile("", "", "")))); - cleanup = new MoveFilesCleanup(databaseContext, "", fileDirPrefs); + when(filePreferences.getFileDirPattern()).thenReturn(""); cleanup.cleanup(entry); - assertFalse(fileBefore.exists()); - File fileAfter = pdfFolder.toPath().resolve("test.pdf").toFile(); - assertTrue(fileAfter.exists()); - + Path fileAfter = defaultFileFolder.resolve("test.pdf"); assertEquals( - Optional.of(FileFieldWriter.getStringRepresentation(new LinkedFile("", fileAfter.getName(), ""))), + Optional.of(FileFieldWriter.getStringRepresentation( + Arrays.asList(new LinkedFile("", "", ""), new LinkedFile("", "test.pdf", ""), new LinkedFile("", "", "")))), entry.getField("file")); + assertFalse(Files.exists(fileBefore)); + assertTrue(Files.exists(fileAfter)); } @Test - void movesFileFromSubfolderWithFileDirPattern(@TempDirectory.TempDir Path bibFolder) throws IOException { - Path path = bibFolder.resolve("AnotherRandomlyNamedFolder"); - Files.createDirectory(path); - File fileBefore = path.resolve("test.pdf").toFile(); - - assertTrue(fileBefore.createNewFile()); - assertTrue(fileBefore.exists()); - - LinkedFile fileField = new LinkedFile("", fileBefore.getAbsolutePath(), ""); - entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - - cleanup = new MoveFilesCleanup(databaseContext, "[entrytype]", fileDirPrefs); - + void movesFileWithFileDirPattern() throws Exception { + when(filePreferences.getFileDirPattern()).thenReturn("[entrytype]"); cleanup.cleanup(entry); - assertFalse(fileBefore.exists()); - Path after = pdfFolder.toPath().resolve("Misc").resolve("test.pdf"); - Path relativefileDir = pdfFolder.toPath().relativize(after); - assertTrue(Files.exists(after)); - - assertEquals(Optional - .of(FileFieldWriter.getStringRepresentation(new LinkedFile("", relativefileDir.toString(), ""))), + Path fileAfter = defaultFileFolder.resolve("Misc").resolve("test.pdf"); + assertEquals( + Optional.of(FileFieldWriter.getStringRepresentation(new LinkedFile("", "Misc/test.pdf", ""))), entry.getField("file")); + assertFalse(Files.exists(fileBefore)); + assertTrue(Files.exists(fileAfter)); } @Test - void movesFileFromSubfolderWithSubdirPattern(@TempDirectory.TempDir Path bibFolder) throws IOException { - BibEntry local_entry = (BibEntry) entry.clone(); - local_entry.setField("year", "1989"); - Path path = bibFolder.resolve("AnotherRandomlyNamedFolder"); - Files.createDirectory(path); - File fileBefore = path.resolve("test.pdf").toFile(); - - assertTrue(fileBefore.createNewFile()); - assertTrue(fileBefore.exists()); - - LinkedFile fileField = new LinkedFile("", fileBefore.getAbsolutePath(), ""); - local_entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - - cleanup = new MoveFilesCleanup(databaseContext, "[year]", fileDirPrefs); - cleanup.cleanup(local_entry); - - assertFalse(fileBefore.exists()); - Path after = pdfFolder.toPath().resolve("1989").resolve("test.pdf"); - Path relativefileDir = pdfFolder.toPath().relativize(after); - assertTrue(Files.exists(after)); - - assertEquals(Optional - .of(FileFieldWriter.getStringRepresentation(new LinkedFile("", relativefileDir.toString(), ""))), - local_entry.getField("file")); - } - - @Test - void movesFileFromSubfolderWithDeepSubdirPattern(@TempDirectory.TempDir Path bibFolder) throws IOException { - BibEntry local_entry = (BibEntry) entry.clone(); - local_entry.setField("year", "1989"); - local_entry.setField("author", "O. Kitsune"); - Path path = bibFolder.resolve("AnotherRandomlyNamedFolder"); - Files.createDirectory(path); - File fileBefore = path.resolve("test.pdf").toFile(); - - assertTrue(fileBefore.createNewFile()); - assertTrue(fileBefore.exists()); - - LinkedFile fileField = new LinkedFile("", fileBefore.getAbsolutePath(), ""); - local_entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - - cleanup = new MoveFilesCleanup(databaseContext, "[entrytype]/[year]/[auth]", fileDirPrefs); - - cleanup.cleanup(local_entry); - - assertFalse(fileBefore.exists()); - Path after = pdfFolder.toPath().resolve("Misc").resolve("1989").resolve("Kitsune").resolve("test.pdf"); - Path relativefileDir = pdfFolder.toPath().relativize(after); - assertTrue(Files.exists(after)); + void movesFileWithSubdirectoryPattern() throws Exception { + when(filePreferences.getFileDirPattern()).thenReturn("[entrytype]/[year]/[auth]"); + cleanup.cleanup(entry); - assertEquals(Optional - .of(FileFieldWriter.getStringRepresentation(new LinkedFile("", relativefileDir.toString(), ""))), - local_entry.getField("file")); + Path fileAfter = defaultFileFolder.resolve("Misc").resolve("1989").resolve("test.pdf"); + assertEquals( + Optional.of(FileFieldWriter.getStringRepresentation(new LinkedFile("", "Misc/1989/test.pdf", ""))), + entry.getField("file")); + assertFalse(Files.exists(fileBefore)); + assertTrue(Files.exists(fileAfter)); } } diff --git a/src/test/java/org/jabref/logic/cleanup/RenamePdfCleanupTest.java b/src/test/java/org/jabref/logic/cleanup/RenamePdfCleanupTest.java index 9c3ad79cb91..eb027f824d0 100644 --- a/src/test/java/org/jabref/logic/cleanup/RenamePdfCleanupTest.java +++ b/src/test/java/org/jabref/logic/cleanup/RenamePdfCleanupTest.java @@ -1,27 +1,24 @@ package org.jabref.logic.cleanup; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Optional; -import org.jabref.logic.layout.LayoutFormatterPreferences; import org.jabref.model.Defaults; import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FileFieldWriter; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.metadata.MetaData; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junitpioneer.jupiter.TempDirectory; -import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -30,24 +27,24 @@ @ExtendWith(TempDirectory.class) class RenamePdfCleanupTest { - private BibDatabaseContext context; private BibEntry entry; - private FileDirectoryPreferences fileDirPrefs; - private LayoutFormatterPreferences layoutFormatterPreferences; + private FilePreferences filePreferences; + private RenamePdfCleanup cleanup; @BeforeEach void setUp(@TempDirectory.TempDir Path testFolder) { Path path = testFolder.resolve("test.bib"); MetaData metaData = new MetaData(); - context = new BibDatabaseContext(new BibDatabase(), metaData, new Defaults()); - context.setDatabaseFile(path.toFile()); + BibDatabaseContext context = new BibDatabaseContext(new BibDatabase(), metaData, new Defaults()); + context.setDatabaseFile(path); - fileDirPrefs = mock(FileDirectoryPreferences.class); - when(fileDirPrefs.isBibLocationAsPrimary()).thenReturn(true); //Set Biblocation as Primary Directory, otherwise the tmp folders won't be cleaned up correctly entry = new BibEntry(); entry.setCiteKey("Toot"); - layoutFormatterPreferences = mock(LayoutFormatterPreferences.class, Answers.RETURNS_DEEP_STUBS); + + filePreferences = mock(FilePreferences.class); + when(filePreferences.isBibLocationAsPrimary()).thenReturn(true); //Set Biblocation as Primary Directory, otherwise the tmp folders won't be cleaned up correctly + cleanup = new RenamePdfCleanup(false, context, filePreferences); } /** @@ -55,15 +52,13 @@ void setUp(@TempDirectory.TempDir Path testFolder) { */ @Test void cleanupRenamePdfRenamesFileEvenIfOnlyDifferenceIsCase(@TempDirectory.TempDir Path testFolder) throws IOException { - String fileNamePattern = "[bibtexkey]"; Path path = testFolder.resolve("toot.tmp"); Files.createFile(path); LinkedFile fileField = new LinkedFile("", path.toAbsolutePath().toString(), ""); entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - RenamePdfCleanup cleanup = new RenamePdfCleanup(false, context, fileNamePattern, - fileDirPrefs); + when(filePreferences.getFileNamePattern()).thenReturn("[bibtexkey]"); cleanup.cleanup(entry); LinkedFile newFileField = new LinkedFile("", "Toot.tmp", ""); @@ -72,27 +67,24 @@ void cleanupRenamePdfRenamesFileEvenIfOnlyDifferenceIsCase(@TempDirectory.TempDi @Test void cleanupRenamePdfRenamesWithMultipleFiles(@TempDirectory.TempDir Path testFolder) throws IOException { - String fileNamePattern = "[bibtexkey] - [fulltitle]"; Path path = testFolder.resolve("Toot.tmp"); Files.createFile(path); - File tempFile = path.toFile(); entry.setField("title", "test title"); - entry.setField("file", FileFieldWriter.getStringRepresentation(Arrays.asList(new LinkedFile("", "", ""), - new LinkedFile("", tempFile.getAbsolutePath(), ""), new LinkedFile("", "", "")))); + entry.setField("file", FileFieldWriter.getStringRepresentation( + Arrays.asList(new LinkedFile("", "", ""), new LinkedFile("", path.toAbsolutePath().toString(), ""), new LinkedFile("", "", "")))); - RenamePdfCleanup cleanup = new RenamePdfCleanup(false, context, fileNamePattern, - fileDirPrefs); + when(filePreferences.getFileNamePattern()).thenReturn("[bibtexkey] - [fulltitle]"); cleanup.cleanup(entry); assertEquals( - Optional.of(FileFieldWriter.getStringRepresentation(new LinkedFile("", "Toot - test title.tmp", ""))), + Optional.of(FileFieldWriter.getStringRepresentation( + Arrays.asList(new LinkedFile("", "", ""), new LinkedFile("", "Toot - test title.tmp", ""), new LinkedFile("", "", "")))), entry.getField("file")); } @Test void cleanupRenamePdfRenamesFileStartingWithBibtexKey(@TempDirectory.TempDir Path testFolder) throws IOException { - String fileNamePattern = "[bibtexkey] - [fulltitle]"; Path path = testFolder.resolve("Toot.tmp"); Files.createFile(path); @@ -100,8 +92,7 @@ void cleanupRenamePdfRenamesFileStartingWithBibtexKey(@TempDirectory.TempDir Pat entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); entry.setField("title", "test title"); - RenamePdfCleanup cleanup = new RenamePdfCleanup(false, context, fileNamePattern, - fileDirPrefs); + when(filePreferences.getFileNamePattern()).thenReturn("[bibtexkey] - [fulltitle]"); cleanup.cleanup(entry); LinkedFile newFileField = new LinkedFile("", "Toot - test title.tmp", ""); @@ -110,49 +101,16 @@ void cleanupRenamePdfRenamesFileStartingWithBibtexKey(@TempDirectory.TempDir Pat @Test void cleanupRenamePdfRenamesFileInSameFolder(@TempDirectory.TempDir Path testFolder) throws IOException { - String fileNamePattern = "[bibtexkey] - [fulltitle]"; Path path = testFolder.resolve("Toot.pdf"); Files.createFile(path); LinkedFile fileField = new LinkedFile("", "Toot.pdf", "PDF"); entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); entry.setField("title", "test title"); - RenamePdfCleanup cleanup = new RenamePdfCleanup(false, context, fileNamePattern, fileDirPrefs); + when(filePreferences.getFileNamePattern()).thenReturn("[bibtexkey] - [fulltitle]"); cleanup.cleanup(entry); LinkedFile newFileField = new LinkedFile("", "Toot - test title.pdf", "PDF"); assertEquals(Optional.of(FileFieldWriter.getStringRepresentation(newFileField)), entry.getField("file")); } - - @Test - void cleanupSingleField(@TempDirectory.TempDir Path testFolder) throws IOException { - String fileNamePattern = "[bibtexkey] - [fulltitle]"; - Path path = testFolder.resolve("Toot.pdf"); - Files.createFile(path); - LinkedFile fileField = new LinkedFile("", "Toot.pdf", "PDF"); - entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - entry.setField("title", "test title"); - RenamePdfCleanup cleanup = new RenamePdfCleanup(false, context, fileNamePattern, - fileDirPrefs, fileField); - - cleanup.cleanup(entry); - - LinkedFile newFileField = new LinkedFile("", "Toot - test title.pdf", "PDF"); - assertEquals(Optional.of(FileFieldWriter.getStringRepresentation(newFileField)), entry.getField("file")); - - } - - @Test - void cleanupGetTargetFilename(@TempDirectory.TempDir Path testFolder) throws IOException { - String fileNamePattern = "[bibtexkey] - [fulltitle]"; - Path path = testFolder.resolve("Toot.pdf"); - Files.createFile(path); - LinkedFile fileField = new LinkedFile("", "Toot.pdf", "PDF"); - RenamePdfCleanup cleanup = new RenamePdfCleanup(false, context, fileNamePattern, fileDirPrefs); - entry.setField("file", FileFieldWriter.getStringRepresentation(fileField)); - entry.setField("title", "test title"); - - assertEquals("Toot - test title.pdf", cleanup.getTargetFileName(fileField, entry)); - } - } diff --git a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java index 04f687f6666..879abb26d7e 100644 --- a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java +++ b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java @@ -19,7 +19,7 @@ import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.InternalBibtexFields; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.metadata.MetaData; import org.junit.jupiter.api.Test; @@ -340,7 +340,7 @@ public void testEntryIsUnchangedAfterChecks() { BibDatabaseContext context = new BibDatabaseContext(bibDatabase, new Defaults()); new IntegrityCheck(context, - mock(FileDirectoryPreferences.class), + mock(FilePreferences.class), createBibtexKeyPatternPreferences(), new JournalAbbreviationRepository(new Abbreviation("IEEE Software", "IEEE SW")), true) .checkBibtexDatabase(); @@ -378,7 +378,7 @@ private BibDatabaseContext createContext(String field, String value) { private void assertWrong(BibDatabaseContext context) { List messages = new IntegrityCheck(context, - mock(FileDirectoryPreferences.class), + mock(FilePreferences.class), createBibtexKeyPatternPreferences(), new JournalAbbreviationRepository(new Abbreviation("IEEE Software", "IEEE SW")), true) .checkBibtexDatabase(); @@ -387,7 +387,7 @@ private void assertWrong(BibDatabaseContext context) { private void assertCorrect(BibDatabaseContext context) { List messages = new IntegrityCheck(context, - mock(FileDirectoryPreferences.class), + mock(FilePreferences.class), createBibtexKeyPatternPreferences(), new JournalAbbreviationRepository(new Abbreviation("IEEE Software", "IEEE SW")), true ).checkBibtexDatabase(); diff --git a/src/test/java/org/jabref/logic/pdf/EntryAnnotationImporterTest.java b/src/test/java/org/jabref/logic/pdf/EntryAnnotationImporterTest.java index f4068cda66f..1a94f42a49a 100644 --- a/src/test/java/org/jabref/logic/pdf/EntryAnnotationImporterTest.java +++ b/src/test/java/org/jabref/logic/pdf/EntryAnnotationImporterTest.java @@ -9,7 +9,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FieldName; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.jabref.model.pdf.FileAnnotation; import org.junit.jupiter.api.BeforeEach; @@ -37,7 +37,7 @@ public void readEntryExampleThesis() { EntryAnnotationImporter entryAnnotationImporter = new EntryAnnotationImporter(entry); //when - Map> annotations = entryAnnotationImporter.importAnnotationsFromFiles(databaseContext, mock(FileDirectoryPreferences.class)); + Map> annotations = entryAnnotationImporter.importAnnotationsFromFiles(databaseContext, mock(FilePreferences.class)); //then int fileCounter = 0; diff --git a/src/test/java/org/jabref/model/database/BibDatabaseContextTest.java b/src/test/java/org/jabref/model/database/BibDatabaseContextTest.java index 8d97da29687..62dcb69d9b9 100644 --- a/src/test/java/org/jabref/model/database/BibDatabaseContextTest.java +++ b/src/test/java/org/jabref/model/database/BibDatabaseContextTest.java @@ -5,7 +5,7 @@ import java.util.Collections; import java.util.List; -import org.jabref.model.metadata.FileDirectoryPreferences; +import org.jabref.model.metadata.FilePreferences; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -20,12 +20,12 @@ public class BibDatabaseContextTest { // Store the minimal preferences for the // BibDatabaseContext.getFileDirectories(File, - // FileDirectoryPreferences) incocation: - private FileDirectoryPreferences fileDirPrefs; + // FilePreferences) incocation: + private FilePreferences fileDirPrefs; @BeforeEach public void setUp() { - fileDirPrefs = mock(FileDirectoryPreferences.class); + fileDirPrefs = mock(FilePreferences.class); currentWorkingDir = Paths.get(System.getProperty("user.dir")); when(fileDirPrefs.isBibLocationAsPrimary()).thenReturn(true); } From 0e1acfe478e1f7160f65bb93e010328c9b879ed3 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 12 Sep 2018 00:14:26 +0200 Subject: [PATCH 2/4] Convert update worker to BackgroundTask (#4334) --- src/main/java/org/jabref/JabRefGUI.java | 12 +- src/main/java/org/jabref/gui/JabRefFrame.java | 2 +- .../gui/actions/SearchForUpdateAction.java | 21 +++- .../org/jabref/gui/help/VersionWorker.java | 94 ++++++++++++++ .../org/jabref/gui/worker/VersionWorker.java | 117 ------------------ 5 files changed, 117 insertions(+), 129 deletions(-) create mode 100644 src/main/java/org/jabref/gui/help/VersionWorker.java delete mode 100644 src/main/java/org/jabref/gui/worker/VersionWorker.java diff --git a/src/main/java/org/jabref/JabRefGUI.java b/src/main/java/org/jabref/JabRefGUI.java index 84ea60e87cb..b8669181f8e 100644 --- a/src/main/java/org/jabref/JabRefGUI.java +++ b/src/main/java/org/jabref/JabRefGUI.java @@ -15,18 +15,17 @@ import org.jabref.gui.GUIGlobals; import org.jabref.gui.JabRefFrame; import org.jabref.gui.dialogs.BackupUIManager; +import org.jabref.gui.help.VersionWorker; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.importer.ParserResultWarningDialog; import org.jabref.gui.importer.actions.OpenDatabaseAction; import org.jabref.gui.shared.SharedDatabaseUIManager; -import org.jabref.gui.worker.VersionWorker; import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.importer.OpenDatabase; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; import org.jabref.logic.shared.exception.InvalidDBMSConnectionPropertiesException; import org.jabref.logic.shared.exception.NotASharedDatabaseException; -import org.jabref.logic.util.Version; import org.jabref.model.database.shared.DatabaseNotSupportedException; import org.jabref.preferences.JabRefPreferences; @@ -60,13 +59,8 @@ public JabRefGUI(Stage mainStage, List argsDatabases, boolean isBl .orElse(Globals.prefs.get(JabRefPreferences.LAST_FOCUSED)); openWindow(mainStage); - JabRefGUI.checkForNewVersion(false); - } - - public static void checkForNewVersion(boolean manualExecution) { - Version toBeIgnored = Globals.prefs.getVersionPreferences().getIgnoredVersion(); - Version currentVersion = Globals.BUILD_INFO.getVersion(); - new VersionWorker(JabRefGUI.getMainFrame(), manualExecution, currentVersion, toBeIgnored).execute(); + new VersionWorker(Globals.BUILD_INFO.getVersion(), Globals.prefs.getVersionPreferences().getIgnoredVersion(), JabRefGUI.getMainFrame().getDialogService(), Globals.TASK_EXECUTOR) + .checkForNewVersionAsync(false); } private void openWindow(Stage mainStage) { diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index d440752b7a2..7e8b983de25 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -954,7 +954,7 @@ private MenuBar createMenu() { new SeparatorMenuItem(), - factory.createMenuItem(StandardActions.SEARCH_FOR_UPDATES, new SearchForUpdateAction()), + factory.createMenuItem(StandardActions.SEARCH_FOR_UPDATES, new SearchForUpdateAction(Globals.BUILD_INFO, prefs.getVersionPreferences(), dialogService, Globals.TASK_EXECUTOR)), factory.createSubMenu(StandardActions.WEB_MENU, factory.createMenuItem(StandardActions.OPEN_WEBPAGE, new OpenBrowserAction("https://jabref.org/")), factory.createMenuItem(StandardActions.OPEN_BLOG, new OpenBrowserAction("https://blog.jabref.org/")), diff --git a/src/main/java/org/jabref/gui/actions/SearchForUpdateAction.java b/src/main/java/org/jabref/gui/actions/SearchForUpdateAction.java index 5a8dc415b74..be3415c2120 100644 --- a/src/main/java/org/jabref/gui/actions/SearchForUpdateAction.java +++ b/src/main/java/org/jabref/gui/actions/SearchForUpdateAction.java @@ -1,11 +1,28 @@ package org.jabref.gui.actions; -import org.jabref.JabRefGUI; +import org.jabref.gui.DialogService; +import org.jabref.gui.help.VersionWorker; +import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.util.BuildInfo; +import org.jabref.preferences.VersionPreferences; public class SearchForUpdateAction extends SimpleCommand { + private final BuildInfo buildInfo; + private final VersionPreferences versionPreferences; + private final DialogService dialogService; + private final TaskExecutor taskExecutor; + + public SearchForUpdateAction(BuildInfo buildInfo, VersionPreferences versionPreferences, DialogService dialogService, TaskExecutor taskExecutor) { + this.buildInfo = buildInfo; + this.versionPreferences = versionPreferences; + this.dialogService = dialogService; + this.taskExecutor = taskExecutor; + } + @Override public void execute() { - JabRefGUI.checkForNewVersion(true); + new VersionWorker(buildInfo.getVersion(), versionPreferences.getIgnoredVersion(), dialogService, taskExecutor) + .checkForNewVersionAsync(true); } } diff --git a/src/main/java/org/jabref/gui/help/VersionWorker.java b/src/main/java/org/jabref/gui/help/VersionWorker.java new file mode 100644 index 00000000000..a77be5cc955 --- /dev/null +++ b/src/main/java/org/jabref/gui/help/VersionWorker.java @@ -0,0 +1,94 @@ +package org.jabref.gui.help; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.jabref.gui.DialogService; +import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.Version; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This worker checks if there is a new version of JabRef available. If there is it will display a dialog to the user + * offering him multiple options to proceed (see changelog, go to the download page, ignore this version, and remind + * later). + * + * If the versions check is executed manually and this is the latest version it will also display a dialog to inform the + * user. + */ +public class VersionWorker { + + private static final Logger LOGGER = LoggerFactory.getLogger(VersionWorker.class); + + /** + * The current version of the installed JabRef + */ + private final Version installedVersion; + + /** + * The version which was previously ignored by the user + */ + private final Version toBeIgnored; + private final DialogService dialogService; + private final TaskExecutor taskExecutor; + + public VersionWorker(Version installedVersion, Version toBeIgnored, DialogService dialogService, TaskExecutor taskExecutor) { + this.installedVersion = Objects.requireNonNull(installedVersion); + this.toBeIgnored = Objects.requireNonNull(toBeIgnored); + this.dialogService = Objects.requireNonNull(dialogService); + this.taskExecutor = Objects.requireNonNull(taskExecutor); + } + + /** + * Returns a newer version excluding any non-stable versions, except if the installed one is unstable too. If no + * newer version was found, then an empty optional is returned. + */ + private Optional getNewVersion() throws IOException { + List availableVersions = Version.getAllAvailableVersions(); + return installedVersion.shouldBeUpdatedTo(availableVersions); + } + + /** + * @param manualExecution if this versions check is executed automatically (eg. on startup) or manually by the user + */ + public void checkForNewVersionAsync(boolean manualExecution) { + BackgroundTask.wrap(this::getNewVersion) + .onSuccess(version -> showUpdateInfo(version, manualExecution)) + .onFailure(exception -> showConnectionError(exception, manualExecution)) + .executeWith(taskExecutor); + } + + /** + * Prints the connection problem to the status bar and shows a dialog if it was executed manually + */ + private void showConnectionError(Exception exception, boolean manualExecution) { + String couldNotConnect = Localization.lang("Could not connect to the update server."); + String tryLater = Localization.lang("Please try again later and/or check your network connection."); + if (manualExecution) { + dialogService.showErrorDialogAndWait(Localization.lang("Error"), couldNotConnect + "\n" + tryLater, exception); + } + LOGGER.warn(couldNotConnect + " " + tryLater, exception); + } + + /** + * Prints up-to-date to the status bar (and shows a dialog it was executed manually) if there is now new version. + * Shows a "New Version" Dialog to the user if there is. + */ + private void showUpdateInfo(Optional newerVersion, boolean manualExecution) { + // no new version could be found, only respect the ignored version on automated version checks + if (!newerVersion.isPresent() || (newerVersion.get().equals(toBeIgnored) && !manualExecution)) { + if (manualExecution) { + dialogService.notify(Localization.lang("JabRef is up-to-date.")); + } + } else { + // notify the user about a newer version + new NewVersionDialog(installedVersion, newerVersion.get()); + } + } +} diff --git a/src/main/java/org/jabref/gui/worker/VersionWorker.java b/src/main/java/org/jabref/gui/worker/VersionWorker.java deleted file mode 100644 index 0926a0d6e50..00000000000 --- a/src/main/java/org/jabref/gui/worker/VersionWorker.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.jabref.gui.worker; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -import javax.swing.JOptionPane; -import javax.swing.SwingWorker; - -import org.jabref.gui.JabRefFrame; -import org.jabref.gui.help.NewVersionDialog; -import org.jabref.gui.util.DefaultTaskExecutor; -import org.jabref.logic.l10n.Localization; -import org.jabref.logic.util.Version; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * This worker checks if there is a new version of JabRef available. - * If there is it will display a Dialog to the User offering him multiple Options to proceed - * (see changelog, go to the download page, ignore this version, and remind later). - * - * If the versions check is executed manually and this is the latest version it will also display a dialog to inform the user. - */ -public class VersionWorker extends SwingWorker, Void> { - - private static final Logger LOGGER = LoggerFactory.getLogger(VersionWorker.class); - - private final JabRefFrame mainFrame; - - /** If this versions check is executed automatically (eg. on startup) or manually by the user */ - private final boolean manualExecution; - - /** The current version of the installed JabRef */ - private final Version installedVersion; - - /** The version which was previously ignored by the user */ - private final Version toBeIgnored; - - - public VersionWorker(JabRefFrame mainFrame, boolean manualExecution, Version installedVersion, Version toBeIgnored) { - this.mainFrame = Objects.requireNonNull(mainFrame); - this.manualExecution = manualExecution; - this.installedVersion = Objects.requireNonNull(installedVersion); - this.toBeIgnored = Objects.requireNonNull(toBeIgnored); - } - - @Override - protected List doInBackground() throws Exception { - try { - return Version.getAllAvailableVersions(); - } catch (IOException ioException) { - LOGGER.warn("Could not connect to the updateserver.", ioException); - return Collections.emptyList(); - } - } - - @Override - public void done() { - if (this.isCancelled()) { - return; - } - - try { - List availableVersions = this.get(); - - // couldn't find any version, connection problems? - if (availableVersions.isEmpty()) { - showConnectionError(); - } else { - showUpdateInfo(availableVersions); - } - - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Error while checking for updates", e); - } - } - - /** - * prints the connection problem to the status bar and shows a dialog if it was executed manually - */ - private void showConnectionError() { - String couldNotConnect = Localization.lang("Could not connect to the update server."); - String tryLater = Localization.lang("Please try again later and/or check your network connection."); - if (manualExecution) { - JOptionPane.showMessageDialog(null, couldNotConnect + "\n" + tryLater, - couldNotConnect, JOptionPane.ERROR_MESSAGE); - } - this.mainFrame.output(couldNotConnect + " " + tryLater); - } - - /** - * Prints up-to-date to the status bar (and shows a dialog it was executed manually) if there is now new version. - * Shows a "New Version" Dialog to the user if there is. - */ - private void showUpdateInfo(List availableVersions) { - // the newer version, excluding any non-stable versions, except if the installed one is unstable too - Optional newerVersion = installedVersion.shouldBeUpdatedTo(availableVersions); - - // no new version could be found, only respect the ignored version on automated version checks - if (!newerVersion.isPresent() || (newerVersion.get().equals(toBeIgnored) && !manualExecution)) { - String upToDate = Localization.lang("JabRef is up-to-date."); - if (manualExecution) { - DefaultTaskExecutor.runInJavaFXThread(() -> mainFrame.getDialogService().showInformationDialogAndWait(upToDate, upToDate)); - } - } else { - // notify the user about a newer version - new NewVersionDialog(installedVersion, newerVersion.get()); - } - } - -} From 8971a4fe79577addbbb6c4582069786ba2c011b6 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 12 Sep 2018 00:14:48 +0200 Subject: [PATCH 3/4] Fix entry editor field display (#4333) * Fix height of some field editors * Make more fields single line --- src/main/java/org/jabref/gui/PreviewPanel.java | 15 +++++++++++---- .../jabref/gui/fieldeditors/BibtexKeyEditor.fxml | 4 ++-- .../jabref/gui/fieldeditors/BibtexKeyEditor.java | 6 +++--- .../jabref/gui/fieldeditors/EditorTextArea.java | 1 - .../jabref/gui/fieldeditors/EditorTextField.java | 7 +++++-- .../jabref/gui/fieldeditors/FieldNameLabel.java | 8 +++----- .../jabref/gui/fieldeditors/JournalEditor.fxml | 4 ++-- .../jabref/gui/fieldeditors/JournalEditor.java | 10 +++++----- .../jabref/model/entry/InternalBibtexFields.java | 6 +++--- 9 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/jabref/gui/PreviewPanel.java b/src/main/java/org/jabref/gui/PreviewPanel.java index cece4cdda37..58fe5a684b2 100644 --- a/src/main/java/org/jabref/gui/PreviewPanel.java +++ b/src/main/java/org/jabref/gui/PreviewPanel.java @@ -150,8 +150,7 @@ public PreviewPanel(BasePanel panel, BibDatabaseContext databaseContext, KeyBind }); createKeyBindings(); - updateLayout(preferences); - + updateLayout(preferences, true); } private void createKeyBindings() { @@ -208,6 +207,10 @@ public void setBasePanel(BasePanel basePanel) { } public void updateLayout(PreviewPreferences previewPreferences) { + updateLayout(previewPreferences, false); + } + + private void updateLayout(PreviewPreferences previewPreferences, boolean init) { if (fixedLayout) { LOGGER.debug("cannot change the layout because the layout is fixed"); return; @@ -220,12 +223,16 @@ public void updateLayout(PreviewPreferences previewPreferences) { CitationStyle.createCitationStyleFromFile(style) .ifPresent(citationStyle -> { basePanel.get().getCitationStyleCache().setCitationStyle(citationStyle); - basePanel.get().output(Localization.lang("Preview style changed to: %0", citationStyle.getTitle())); + if (!init) { + basePanel.get().output(Localization.lang("Preview style changed to: %0", citationStyle.getTitle())); + } }); } } else { updatePreviewLayout(previewPreferences.getPreviewStyle(), previewPreferences.getLayoutFormatterPreferences()); - basePanel.ifPresent(panel -> panel.output(Localization.lang("Preview style changed to: %0", Localization.lang("Preview")))); + if (!init) { + basePanel.ifPresent(panel -> panel.output(Localization.lang("Preview style changed to: %0", Localization.lang("Preview")))); + } } update(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml b/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml index 211549c7ac5..99d6d3f7601 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml +++ b/src/main/java/org/jabref/gui/fieldeditors/BibtexKeyEditor.fxml @@ -2,9 +2,9 @@ - + - +