diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d5069e2517..83579f5575d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We changed the behavior of the field formatting dialog such that the `bibtexkey` is not changed when formatting all fields or all text fields. - We added a "Move file to file directory and rename file" option for simultaneously moving and renaming of document file. [#4166](https://github.com/JabRef/jabref/issues/4166) - Use integrated graphics card instead of discrete on macOS [#4070](https://github.com/JabRef/jabref/issues/4070) +- We added a cleanup operation that detects an arXiv identifier in the note, journal or url field and moves it to the `eprint` field. + Because of this change, the last-used cleanup operations were reset. - We changed the minimum required version of Java to 1.8.0_171, as this is the latest release for which the automatic Java update works. [4093](https://github.com/JabRef/jabref/issues/4093) - The special fields like `Printed` and `Read status` now show gray icons when the row is hovered. - We added a button in the tab header which allows you to close the database with one click. https://github.com/JabRef/jabref/issues/494 diff --git a/src/main/java/org/jabref/gui/actions/CleanupAction.java b/src/main/java/org/jabref/gui/actions/CleanupAction.java index a4814735c8d..648af8313b7 100644 --- a/src/main/java/org/jabref/gui/actions/CleanupAction.java +++ b/src/main/java/org/jabref/gui/actions/CleanupAction.java @@ -10,7 +10,6 @@ import org.jabref.gui.undo.NamedCompound; import org.jabref.gui.undo.UndoableFieldChange; import org.jabref.gui.util.BackgroundTask; -import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.logic.cleanup.CleanupPreset; import org.jabref.logic.cleanup.CleanupWorker; import org.jabref.logic.l10n.Localization; @@ -23,11 +22,6 @@ public class CleanupAction implements BaseAction { private final BasePanel panel; private final DialogService dialogService; - /** - * Global variable to count unsuccessful renames - */ - private int unsuccessfulRenames; - private boolean isCanceled; private int modifiedEntriesCount; private final JabRefPreferences preferences; @@ -44,13 +38,31 @@ public void action() { if (isCanceled) { return; } - CleanupDialog cleanupDialog = new CleanupDialog(panel.getBibDatabaseContext(), preferences.getCleanupPreset()); + CleanupDialog cleanupDialog = new CleanupDialog(panel.getBibDatabaseContext(), preferences.getCleanupPreset(), preferences.getFilePreferences()); Optional chosenPreset = cleanupDialog.showAndWait(); - chosenPreset.ifPresent(cleanupPreset -> - BackgroundTask.wrap(() -> cleanup(cleanupPreset)) - .onSuccess(x -> showResults()) - .executeWith(Globals.TASK_EXECUTOR)); + + if (chosenPreset.isPresent()) { + if (chosenPreset.get().isRenamePDFActive() && preferences.getBoolean(JabRefPreferences.ASK_AUTO_NAMING_PDFS_AGAIN)) { + boolean confirmed = dialogService.showConfirmationDialogWithOptOutAndWait(Localization.lang("Autogenerate PDF Names"), + Localization.lang("Auto-generating PDF-Names does not support undo. Continue?"), + Localization.lang("Autogenerate PDF Names"), + Localization.lang("Cancel"), + Localization.lang("Disable this confirmation dialog"), + optOut -> Globals.prefs.putBoolean(JabRefPreferences.ASK_AUTO_NAMING_PDFS_AGAIN, !optOut)); + + if (!confirmed) { + isCanceled = true; + return; + } + } + + preferences.setCleanupPreset(chosenPreset.get()); + + BackgroundTask.wrap(() -> cleanup(chosenPreset.get())) + .onSuccess(result -> showResults()) + .executeWith(Globals.TASK_EXECUTOR); + } } public void init() { @@ -111,21 +123,6 @@ private void showResults() { private void cleanup(CleanupPreset cleanupPreset) { preferences.setCleanupPreset(cleanupPreset); - if (cleanupPreset.isRenamePDF() && preferences.getBoolean(JabRefPreferences.ASK_AUTO_NAMING_PDFS_AGAIN)) { - - boolean confirmed = DefaultTaskExecutor.runInJavaFXThread(() -> dialogService.showConfirmationDialogWithOptOutAndWait(Localization.lang("Autogenerate PDF Names"), - Localization.lang("Auto-generating PDF-Names does not support undo. Continue?"), - Localization.lang("Autogenerate PDF Names"), - Localization.lang("Cancel"), - Localization.lang("Disable this confirmation dialog"), - optOut -> Globals.prefs.putBoolean(JabRefPreferences.ASK_AUTO_NAMING_PDFS_AGAIN, !optOut))); - - if (!confirmed) { - isCanceled = true; - return; - } - } - for (BibEntry entry : panel.getSelectedEntries()) { // undo granularity is on entry level NamedCompound ce = new NamedCompound(Localization.lang("Cleanup entry")); diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java b/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java index 4e672761366..83d9e810b05 100644 --- a/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java +++ b/src/main/java/org/jabref/gui/cleanup/CleanupDialog.java @@ -6,15 +6,16 @@ import org.jabref.logic.cleanup.CleanupPreset; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.metadata.FilePreferences; public class CleanupDialog extends BaseDialog { - public CleanupDialog(BibDatabaseContext databaseContext, CleanupPreset initialPreset) { + public CleanupDialog(BibDatabaseContext databaseContext, CleanupPreset initialPreset, FilePreferences filePreferences) { setTitle(Localization.lang("Cleanup entries")); getDialogPane().setPrefSize(600, 600); getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL); - CleanupPresetPanel presetPanel = new CleanupPresetPanel(databaseContext, initialPreset); + CleanupPresetPanel presetPanel = new CleanupPresetPanel(databaseContext, initialPreset, filePreferences); getDialogPane().setContent(presetPanel); setResultConverter(button -> { if (button == ButtonType.OK) { diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml new file mode 100644 index 00000000000..1ca9d6ebafa --- /dev/null +++ b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java index 4bd64ec1f62..9149ef4a5f0 100644 --- a/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java +++ b/src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java @@ -6,124 +6,103 @@ import java.util.Optional; import java.util.Set; -import javafx.scene.Group; +import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.layout.GridPane; +import javafx.scene.layout.VBox; -import org.jabref.Globals; import org.jabref.logic.cleanup.CleanupPreset; import org.jabref.logic.cleanup.Cleanups; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.FieldName; -import org.jabref.preferences.JabRefPreferences; +import org.jabref.model.metadata.FilePreferences; -public class CleanupPresetPanel extends ScrollPane { +import com.airhacks.afterburner.views.ViewLoader; + +public class CleanupPresetPanel extends VBox { private final BibDatabaseContext databaseContext; - private CheckBox cleanUpDOI; - private CheckBox cleanUpISSN; - private CheckBox cleanUpMovePDF; - private CheckBox cleanUpMakePathsRelative; - private CheckBox cleanUpRenamePDF; - private CheckBox cleanUpRenamePDFonlyRelativePaths; - private CheckBox cleanUpUpgradeExternalLinks; - private CheckBox cleanUpBiblatex; - private CheckBox cleanUpBibtex; + @FXML private Label cleanupRenamePDFLabel; + @FXML private CheckBox cleanUpDOI; + @FXML private CheckBox cleanUpEprint; + @FXML private CheckBox cleanUpISSN; + @FXML private CheckBox cleanUpMovePDF; + @FXML private CheckBox cleanUpMakePathsRelative; + @FXML private CheckBox cleanUpRenamePDF; + @FXML private CheckBox cleanUpRenamePDFonlyRelativePaths; + @FXML private CheckBox cleanUpUpgradeExternalLinks; + @FXML private CheckBox cleanUpBiblatex; + @FXML private CheckBox cleanUpBibtex; + @FXML private VBox formatterContainer; private FieldFormatterCleanupsPanel cleanUpFormatters; - private CleanupPreset cleanupPreset; - - public CleanupPresetPanel(BibDatabaseContext databaseContext, CleanupPreset cleanupPreset) { - this.cleanupPreset = Objects.requireNonNull(cleanupPreset); + public CleanupPresetPanel(BibDatabaseContext databaseContext, CleanupPreset cleanupPreset, FilePreferences filePreferences) { this.databaseContext = Objects.requireNonNull(databaseContext); - init(); + + // Load FXML + ViewLoader.view(this) + .root(this) + .load(); + + init(cleanupPreset, filePreferences); } - private void init() { - cleanUpDOI = new CheckBox( - Localization.lang("Move DOIs from note and URL field to DOI field and remove http prefix")); - cleanUpISSN = new CheckBox(Localization.lang("Reformat ISSN")); - Optional firstExistingDir = databaseContext - .getFirstExistingFileDir(JabRefPreferences.getInstance().getFilePreferences()); + private void init(CleanupPreset cleanupPreset, FilePreferences filePreferences) { + Optional firstExistingDir = databaseContext.getFirstExistingFileDir(filePreferences); if (firstExistingDir.isPresent()) { - cleanUpMovePDF = new CheckBox(Localization.lang("Move linked files to default file directory %0", - firstExistingDir.get().toString())); + cleanUpMovePDF.setText(Localization.lang("Move linked files to default file directory %0", firstExistingDir.get().toString())); } else { - cleanUpMovePDF = new CheckBox(Localization.lang("Move linked files to default file directory %0", "...")); - cleanUpMovePDF.setDisable(true); + cleanUpMovePDF.setText(Localization.lang("Move linked files to default file directory %0", "...")); + // Since the directory does not exist, we cannot move it to there. So, this option is not checked - regardless of the presets stored in the preferences. + cleanUpMovePDF.setDisable(true); cleanUpMovePDF.setSelected(false); } - cleanUpMakePathsRelative = new CheckBox( - Localization.lang("Make paths of linked files relative (if possible)")); - cleanUpRenamePDF = new CheckBox(Localization.lang("Rename PDFs to given filename format pattern")); - cleanUpRenamePDF.selectedProperty().addListener( - event -> cleanUpRenamePDFonlyRelativePaths.setDisable(!cleanUpRenamePDF.isSelected())); - cleanUpRenamePDFonlyRelativePaths = new CheckBox(Localization.lang("Rename only PDFs having a relative path")); - cleanUpUpgradeExternalLinks = new CheckBox( - Localization.lang("Upgrade external PDF/PS links to use the '%0' field.", FieldName.FILE)); - cleanUpBiblatex = new CheckBox(Localization.lang( - "Convert to biblatex format (for example, move the value of the 'journal' field to 'journaltitle')")); - cleanUpBibtex = new CheckBox(Localization.lang( - "Convert to BibTeX format (for example, move the value of the 'journaltitle' field to 'journal')")); - Group biblatexConversion = new Group(); // Only make "to Biblatex" or "to BibTeX" selectable - biblatexConversion.getChildren().add(cleanUpBiblatex); - biblatexConversion.getChildren().add(cleanUpBibtex); - - cleanUpFormatters = new FieldFormatterCleanupsPanel(Localization.lang("Run field formatter:"), - Cleanups.DEFAULT_SAVE_ACTIONS); - updateDisplay(cleanupPreset); + cleanUpRenamePDFonlyRelativePaths.disableProperty().bind(cleanUpRenamePDF.selectedProperty().not()); - GridPane container = new GridPane(); - container.add(cleanUpDOI, 0, 0); - container.add(cleanUpUpgradeExternalLinks, 0, 1); - container.add(cleanUpMovePDF, 0, 2); - container.add(cleanUpMakePathsRelative, 0, 3); - container.add(cleanUpRenamePDF, 0, 4); - String currentPattern = Localization.lang("Filename format pattern").concat(": "); - currentPattern = currentPattern.concat(Globals.prefs.get(JabRefPreferences.IMPORT_FILENAMEPATTERN)); - container.add(new Label(currentPattern), 0, 5); - container.add(cleanUpRenamePDFonlyRelativePaths, 0, 6); - container.add(cleanUpBibtex, 0, 7); - container.add(cleanUpBiblatex, 0, 8); - container.add(cleanUpISSN, 0, 9); - container.add(cleanUpFormatters, 0, 10); - - setContent(container); - setVbarPolicy(ScrollBarPolicy.AS_NEEDED); + cleanUpUpgradeExternalLinks.setText(Localization.lang("Upgrade external PDF/PS links to use the '%0' field.", FieldName.FILE)); + + cleanUpFormatters = new FieldFormatterCleanupsPanel(Localization.lang("Run field formatter:"), Cleanups.DEFAULT_SAVE_ACTIONS); + formatterContainer.getChildren().setAll(cleanUpFormatters); + + String currentPattern = Localization.lang("Filename format pattern") + .concat(": ") + .concat(filePreferences.getFileNamePattern()); + cleanupRenamePDFLabel.setText(currentPattern); + + updateDisplay(cleanupPreset); } private void updateDisplay(CleanupPreset preset) { - cleanUpDOI.setSelected(preset.isCleanUpDOI()); + cleanUpDOI.setSelected(preset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_DOI)); + cleanUpEprint.setSelected(preset.isActive(CleanupPreset.CleanupStep.CLEANUP_EPRINT)); if (!cleanUpMovePDF.isDisabled()) { - cleanUpMovePDF.setSelected(preset.isMovePDF()); + cleanUpMovePDF.setSelected(preset.isActive(CleanupPreset.CleanupStep.MOVE_PDF)); } - cleanUpMakePathsRelative.setSelected(preset.isMakePathsRelative()); - cleanUpRenamePDF.setSelected(preset.isRenamePDF()); - cleanUpRenamePDFonlyRelativePaths.setSelected(preset.isRenamePdfOnlyRelativePaths()); - cleanUpRenamePDFonlyRelativePaths.setDisable(!cleanUpRenamePDF.isSelected()); - cleanUpUpgradeExternalLinks.setSelected(preset.isCleanUpUpgradeExternalLinks()); - cleanUpBiblatex.setSelected(preset.isConvertToBiblatex()); - cleanUpBibtex.setSelected(preset.isConvertToBibtex()); - cleanUpISSN.setSelected(preset.isCleanUpISSN()); + cleanUpMakePathsRelative.setSelected(preset.isActive(CleanupPreset.CleanupStep.MAKE_PATHS_RELATIVE)); + cleanUpRenamePDF.setSelected(preset.isRenamePDFActive()); + cleanUpRenamePDFonlyRelativePaths.setSelected(preset.isActive(CleanupPreset.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS)); + cleanUpUpgradeExternalLinks.setSelected(preset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS)); + cleanUpBiblatex.setSelected(preset.isActive(CleanupPreset.CleanupStep.CONVERT_TO_BIBLATEX)); + cleanUpBibtex.setSelected(preset.isActive(CleanupPreset.CleanupStep.CONVERT_TO_BIBTEX)); + cleanUpISSN.setSelected(preset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_ISSN)); cleanUpFormatters.setValues(preset.getFormatterCleanups()); } public CleanupPreset getCleanupPreset() { - Set activeJobs = EnumSet.noneOf(CleanupPreset.CleanupStep.class); if (cleanUpMovePDF.isSelected()) { activeJobs.add(CleanupPreset.CleanupStep.MOVE_PDF); } - if (cleanUpDOI.isSelected()) { activeJobs.add(CleanupPreset.CleanupStep.CLEAN_UP_DOI); } + if (cleanUpEprint.isSelected()) { + activeJobs.add(CleanupPreset.CleanupStep.CLEANUP_EPRINT); + } if (cleanUpISSN.isSelected()) { activeJobs.add(CleanupPreset.CleanupStep.CLEAN_UP_ISSN); } @@ -149,7 +128,6 @@ public CleanupPreset getCleanupPreset() { activeJobs.add(CleanupPreset.CleanupStep.FIX_FILE_LINKS); - cleanupPreset = new CleanupPreset(activeJobs, cleanUpFormatters.getFormatterCleanups()); - return cleanupPreset; + return new CleanupPreset(activeJobs, cleanUpFormatters.getFormatterCleanups()); } } diff --git a/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java b/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java index b2aaec4108c..ab544366e0d 100644 --- a/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java +++ b/src/main/java/org/jabref/gui/cleanup/FieldFormatterCleanupsPanel.java @@ -103,7 +103,7 @@ private void buildLayout() { actionsList = new ListView<>(actions); actionsList.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); new ViewModelListCellFactory() - .withText(action -> action.getFormatter().getName()) + .withText(action -> action.getField() + ": " + action.getFormatter().getName()) .withTooltip(action -> action.getFormatter().getDescription()) .install(actionsList); add(actionsList, 1, 1, 3, 1); diff --git a/src/main/java/org/jabref/logic/cleanup/CleanupPreset.java b/src/main/java/org/jabref/logic/cleanup/CleanupPreset.java index 06db606b591..a326dc6eed8 100644 --- a/src/main/java/org/jabref/logic/cleanup/CleanupPreset.java +++ b/src/main/java/org/jabref/logic/cleanup/CleanupPreset.java @@ -1,6 +1,7 @@ package org.jabref.logic.cleanup; import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; import java.util.Objects; import java.util.Set; @@ -12,7 +13,6 @@ public class CleanupPreset { private final Set activeJobs; private final FieldFormatterCleanups formatterCleanups; - public CleanupPreset(Set activeJobs) { this(activeJobs, new FieldFormatterCleanups(false, new ArrayList<>())); } @@ -30,46 +30,14 @@ public CleanupPreset(Set activeJobs, FieldFormatterCleanups formatt this.formatterCleanups = Objects.requireNonNull(formatterCleanups); } - public boolean isCleanUpUpgradeExternalLinks() { - return isActive(CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS); - } - - public boolean isCleanUpDOI() { - return isActive(CleanupStep.CLEAN_UP_DOI); - } - - public boolean isCleanUpISSN() { - return isActive(CleanupStep.CLEAN_UP_ISSN); - } - - public boolean isFixFileLinks() { - return isActive(CleanupStep.FIX_FILE_LINKS); - } - - public boolean isMovePDF() { - return isActive(CleanupStep.MOVE_PDF); + public Set getActiveJobs() { + return Collections.unmodifiableSet(activeJobs); } - public boolean isMakePathsRelative() { - return isActive(CleanupStep.MAKE_PATHS_RELATIVE); - } - - public boolean isRenamePDF() { + public boolean isRenamePDFActive() { return isActive(CleanupStep.RENAME_PDF) || isActive(CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS); } - public boolean isConvertToBiblatex() { - return isActive(CleanupStep.CONVERT_TO_BIBLATEX); - } - - public boolean isConvertToBibtex() { - return isActive(CleanupStep.CONVERT_TO_BIBTEX); - } - - public boolean isRenamePdfOnlyRelativePaths() { - return isActive(CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS); - } - public Boolean isActive(CleanupStep step) { return activeJobs.contains(step); } @@ -83,6 +51,7 @@ public enum CleanupStep { * Removes the http://... for each DOI. Moves DOIs from URL and NOTE filed to DOI field. */ CLEAN_UP_DOI, + CLEANUP_EPRINT, MAKE_PATHS_RELATIVE, RENAME_PDF, RENAME_PDF_ONLY_RELATIVE_PATHS, @@ -102,5 +71,4 @@ public enum CleanupStep { FIX_FILE_LINKS, CLEAN_UP_ISSN } - } diff --git a/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java b/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java index c07650436d1..1831064a286 100644 --- a/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java +++ b/src/main/java/org/jabref/logic/cleanup/CleanupWorker.java @@ -37,38 +37,43 @@ public List cleanup(CleanupPreset preset, BibEntry entry) { private List determineCleanupActions(CleanupPreset preset) { List jobs = new ArrayList<>(); - if (preset.isConvertToBiblatex()) { - jobs.add(new ConvertToBiblatexCleanup()); - } - if (preset.isConvertToBibtex()) { - jobs.add(new ConvertToBibtexCleanup()); + for (CleanupPreset.CleanupStep action : preset.getActiveJobs()) { + jobs.add(toJob(action)); } + if (preset.getFormatterCleanups().isEnabled()) { jobs.addAll(preset.getFormatterCleanups().getConfiguredActions()); } - if (preset.isCleanUpUpgradeExternalLinks()) { - jobs.add(new UpgradePdfPsToFileCleanup()); - } - if (preset.isCleanUpDOI()) { - jobs.add(new DoiCleanup()); - } - if (preset.isCleanUpISSN()) { - jobs.add(new ISSNCleanup()); - } - if (preset.isFixFileLinks()) { - jobs.add(new FileLinksCleanup()); - } - if (preset.isMovePDF()) { - jobs.add(new MoveFilesCleanup(databaseContext, filePreferences)); - } - if (preset.isMakePathsRelative()) { - jobs.add(new RelativePathsCleanup(databaseContext, filePreferences)); - } - if (preset.isRenamePDF()) { - RenamePdfCleanup cleaner = new RenamePdfCleanup(preset.isRenamePdfOnlyRelativePaths(), databaseContext, filePreferences); - jobs.add(cleaner); - } return jobs; } + + private CleanupJob toJob(CleanupPreset.CleanupStep action) { + switch (action) { + case CLEAN_UP_DOI: + return new DoiCleanup(); + case CLEANUP_EPRINT: + return new EprintCleanup(); + case MAKE_PATHS_RELATIVE: + return new RelativePathsCleanup(databaseContext, filePreferences); + case RENAME_PDF: + return new RenamePdfCleanup(false, databaseContext, filePreferences); + case RENAME_PDF_ONLY_RELATIVE_PATHS: + return new RenamePdfCleanup(true, databaseContext, filePreferences); + case CLEAN_UP_UPGRADE_EXTERNAL_LINKS: + return new UpgradePdfPsToFileCleanup(); + case CONVERT_TO_BIBLATEX: + return new ConvertToBiblatexCleanup(); + case CONVERT_TO_BIBTEX: + return new ConvertToBibtexCleanup(); + case MOVE_PDF: + return new MoveFilesCleanup(databaseContext, filePreferences); + case FIX_FILE_LINKS: + return new FileLinksCleanup(); + case CLEAN_UP_ISSN: + return new ISSNCleanup(); + default: + throw new UnsupportedOperationException(action.name()); + } + } } diff --git a/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java b/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java index 03417e00e3e..9bee454b409 100644 --- a/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java +++ b/src/main/java/org/jabref/logic/cleanup/DoiCleanup.java @@ -55,14 +55,9 @@ public List cleanup(BibEntry entry) { Optional doi = entry.getField(field).flatMap(DOI::parse); if (doi.isPresent()) { - // update Doi - String oldValue = entry.getField(FieldName.DOI).orElse(null); - String newValue = doi.get().getDOI(); - - entry.setField(FieldName.DOI, newValue); - - FieldChange change = new FieldChange(entry, FieldName.DOI, oldValue, newValue); - changes.add(change); + // Update Doi + Optional change = entry.setField(FieldName.DOI, doi.get().getDOI()); + change.ifPresent(changes::add); removeFieldValue(entry, field, changes); } diff --git a/src/main/java/org/jabref/logic/cleanup/EprintCleanup.java b/src/main/java/org/jabref/logic/cleanup/EprintCleanup.java new file mode 100644 index 00000000000..973fc1d0705 --- /dev/null +++ b/src/main/java/org/jabref/logic/cleanup/EprintCleanup.java @@ -0,0 +1,52 @@ +package org.jabref.logic.cleanup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.jabref.model.FieldChange; +import org.jabref.model.cleanup.CleanupJob; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.FieldName; +import org.jabref.model.entry.identifier.ArXivIdentifier; + +/** + * Formats the DOI (e.g. removes http part) and also moves DOIs from note, url or ee field to the doi field. + */ +public class EprintCleanup implements CleanupJob { + + @Override + public List cleanup(BibEntry entry) { + + List changes = new ArrayList<>(); + + for (String field : Arrays.asList(FieldName.URL, FieldName.JOURNAL, FieldName.JOURNALTITLE, FieldName.NOTE)) { + Optional arXivIdentifier = entry.getField(field).flatMap(ArXivIdentifier::parse); + + if (arXivIdentifier.isPresent()) { + entry.setField(FieldName.EPRINT, arXivIdentifier.get().getNormalized()) + .ifPresent(changes::add); + + entry.setField(FieldName.EPRINTTYPE, "arxiv") + .ifPresent(changes::add); + + arXivIdentifier.get().getClassification().ifPresent(classification -> + entry.setField(FieldName.EPRINTCLASS, classification) + .ifPresent(changes::add) + ); + + entry.clearField(field) + .ifPresent(changes::add); + + if (field.equals(FieldName.URL)) { + // If we clear the URL field, we should also clear the URL-date field + entry.clearField(FieldName.URLDATE) + .ifPresent(changes::add); + } + } + } + + return changes; + } +} diff --git a/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java b/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java index 8b9287d3e2b..ba01e496518 100644 --- a/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java +++ b/src/main/java/org/jabref/model/entry/identifier/ArXivIdentifier.java @@ -4,20 +4,72 @@ import java.net.URISyntaxException; import java.util.Objects; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.jabref.model.entry.FieldName; +/** + * Identifier for the arXiv. See https://arxiv.org/help/arxiv_identifier + */ public class ArXivIdentifier implements Identifier { private final String identifier; + private final String classification; ArXivIdentifier(String identifier) { - this.identifier = Objects.requireNonNull(identifier).trim(); + this(identifier, ""); + } + + ArXivIdentifier(String identifier, String classification) { + this.identifier = identifier.trim(); + this.classification = classification.trim(); } public static Optional parse(String value) { - String identifier = value.replaceAll("(?i)arxiv:", ""); - return Optional.of(new ArXivIdentifier(identifier)); + Pattern identifierPattern = Pattern.compile("(arxiv|arXiv)?\\s?:?\\s?(?\\d{4}.\\d{4,5}(v\\d+)?)\\s?(\\[(?\\S+)\\])?"); + Matcher identifierMatcher = identifierPattern.matcher(value); + if (identifierMatcher.matches()) { + String id = identifierMatcher.group("id"); + String classification = identifierMatcher.group("classification"); + if (classification == null) { + classification = ""; + } + return Optional.of(new ArXivIdentifier(id, classification)); + } + + Pattern oldIdentifierPattern = Pattern.compile("(arxiv|arXiv)?\\s?:?\\s?(?(?[a-z\\-]+(\\.[A-Z]{2})?)/\\d{7})"); + Matcher oldIdentifierMatcher = oldIdentifierPattern.matcher(value); + if (oldIdentifierMatcher.matches()) { + String id = oldIdentifierMatcher.group("id"); + String classification = oldIdentifierMatcher.group("classification"); + return Optional.of(new ArXivIdentifier(id, classification)); + } + + Pattern urlPattern = Pattern.compile("(http://arxiv.org/abs/)(?\\S+)"); + Matcher urlMatcher = urlPattern.matcher(value); + if (urlMatcher.matches()) { + String id = urlMatcher.group("id"); + return Optional.of(new ArXivIdentifier(id)); + } + + return Optional.empty(); + } + + public Optional getClassification() { + if (classification.isEmpty()) { + return Optional.empty(); + } else { + return Optional.of(classification); + } + } + + @Override + public String toString() { + return "ArXivIdentifier{" + + "identifier='" + identifier + '\'' + + ", classification='" + classification + '\'' + + '}'; } @Override @@ -25,18 +77,18 @@ public boolean equals(Object o) { if (this == o) { return true; } - if ((o == null) || (getClass() != o.getClass())) { + if (o == null || getClass() != o.getClass()) { return false; } ArXivIdentifier that = (ArXivIdentifier) o; - - return identifier.equals(that.identifier); + return Objects.equals(identifier, that.identifier) && + Objects.equals(classification, that.classification); } @Override public int hashCode() { - return identifier.hashCode(); + return Objects.hash(identifier, classification); } @Override @@ -49,11 +101,6 @@ public String getNormalized() { return identifier; } - @Override - public String toString() { - return "ArXivIdentifier [identifier=" + identifier + "]"; - } - @Override public Optional getExternalURI() { try { diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index eea8a2ee771..65024a89f13 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -286,16 +286,7 @@ public class JabRefPreferences implements PreferencesService { public static final String USE_UNIT_FORMATTER_ON_SEARCH = "useUnitFormatterOnSearch"; public static final String USE_CASE_KEEPER_ON_SEARCH = "useCaseKeeperOnSearch"; public static final String ASK_AUTO_NAMING_PDFS_AGAIN = "AskAutoNamingPDFsAgain"; - public static final String CLEANUP_DOI = "CleanUpDOI"; - public static final String CLEANUP_ISSN = "CleanUpISSN"; - public static final String CLEANUP_MOVE_PDF = "CleanUpMovePDF"; - public static final String CLEANUP_MAKE_PATHS_RELATIVE = "CleanUpMakePathsRelative"; - public static final String CLEANUP_RENAME_PDF = "CleanUpRenamePDF"; - public static final String CLEANUP_RENAME_PDF_ONLY_RELATIVE_PATHS = "CleanUpRenamePDFonlyRelativePaths"; - public static final String CLEANUP_UPGRADE_EXTERNAL_LINKS = "CleanUpUpgradeExternalLinks"; - public static final String CLEANUP_CONVERT_TO_BIBLATEX = "CleanUpConvertToBiblatex"; - public static final String CLEANUP_CONVERT_TO_BIBTEX = "CleanUpConvertToBibtex"; - public static final String CLEANUP_FIX_FILE_LINKS = "CleanUpFixFileLinks"; + public static final String CLEANUP = "CleanUp"; public static final String CLEANUP_FORMATTERS = "CleanUpFormatters"; public static final String IMPORT_DEFAULT_PDF_IMPORT_STYLE = "importDefaultPDFimportStyle"; public static final String IMPORT_ALWAYSUSE = "importAlwaysUsePDFImportStyle"; @@ -887,25 +878,16 @@ private static Optional getNextUnit(Reader data) throws IOException { private static void insertDefaultCleanupPreset(Map storage) { EnumSet deactivatedJobs = EnumSet.of( - CleanupPreset.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS, - CleanupPreset.CleanupStep.MOVE_PDF, - CleanupPreset.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS, - CleanupPreset.CleanupStep.CONVERT_TO_BIBLATEX, - CleanupPreset.CleanupStep.CONVERT_TO_BIBTEX); - - CleanupPreset preset = new CleanupPreset(EnumSet.complementOf(deactivatedJobs), Cleanups.DEFAULT_SAVE_ACTIONS); - - storage.put(CLEANUP_DOI, preset.isCleanUpDOI()); - storage.put(CLEANUP_ISSN, preset.isCleanUpISSN()); - storage.put(CLEANUP_MOVE_PDF, preset.isMovePDF()); - storage.put(CLEANUP_MAKE_PATHS_RELATIVE, preset.isMakePathsRelative()); - storage.put(CLEANUP_RENAME_PDF, preset.isRenamePDF()); - storage.put(CLEANUP_RENAME_PDF_ONLY_RELATIVE_PATHS, preset.isRenamePdfOnlyRelativePaths()); - storage.put(CLEANUP_UPGRADE_EXTERNAL_LINKS, preset.isCleanUpUpgradeExternalLinks()); - storage.put(CLEANUP_CONVERT_TO_BIBLATEX, preset.isConvertToBiblatex()); - storage.put(CLEANUP_CONVERT_TO_BIBTEX, preset.isConvertToBibtex()); - storage.put(CLEANUP_FIX_FILE_LINKS, preset.isFixFileLinks()); - storage.put(CLEANUP_FORMATTERS, convertListToString(preset.getFormatterCleanups().getAsStringList(OS.NEWLINE))); + CleanupPreset.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS, + CleanupPreset.CleanupStep.MOVE_PDF, + CleanupPreset.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS, + CleanupPreset.CleanupStep.CONVERT_TO_BIBLATEX, + CleanupPreset.CleanupStep.CONVERT_TO_BIBTEX); + + for (CleanupPreset.CleanupStep action : EnumSet.allOf(CleanupPreset.CleanupStep.class)) { + storage.put(JabRefPreferences.CLEANUP + action.name(), !deactivatedJobs.contains(action)); + } + storage.put(CLEANUP_FORMATTERS, convertListToString(Cleanups.DEFAULT_SAVE_ACTIONS.getAsStringList(OS.NEWLINE))); } public EntryEditorPreferences getEntryEditorPreferences() { @@ -1686,57 +1668,23 @@ public CleanupPreferences getCleanupPreferences(JournalAbbreviationLoader journa public CleanupPreset getCleanupPreset() { Set activeJobs = EnumSet.noneOf(CleanupPreset.CleanupStep.class); - if (this.getBoolean(JabRefPreferences.CLEANUP_DOI)) { - activeJobs.add(CleanupPreset.CleanupStep.CLEAN_UP_DOI); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_ISSN)) { - activeJobs.add(CleanupPreset.CleanupStep.CLEAN_UP_ISSN); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_MOVE_PDF)) { - activeJobs.add(CleanupPreset.CleanupStep.MOVE_PDF); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_MAKE_PATHS_RELATIVE)) { - activeJobs.add(CleanupPreset.CleanupStep.MAKE_PATHS_RELATIVE); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_RENAME_PDF)) { - activeJobs.add(CleanupPreset.CleanupStep.RENAME_PDF); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_RENAME_PDF_ONLY_RELATIVE_PATHS)) { - activeJobs.add(CleanupPreset.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_UPGRADE_EXTERNAL_LINKS)) { - activeJobs.add(CleanupPreset.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_CONVERT_TO_BIBLATEX)) { - activeJobs.add(CleanupPreset.CleanupStep.CONVERT_TO_BIBLATEX); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_CONVERT_TO_BIBTEX)) { - activeJobs.add(CleanupPreset.CleanupStep.CONVERT_TO_BIBTEX); - } - if (this.getBoolean(JabRefPreferences.CLEANUP_FIX_FILE_LINKS)) { - activeJobs.add(CleanupPreset.CleanupStep.FIX_FILE_LINKS); + for (CleanupPreset.CleanupStep action : EnumSet.allOf(CleanupPreset.CleanupStep.class)) { + if (getBoolean(JabRefPreferences.CLEANUP + action.name())) { + activeJobs.add(action); + } } - FieldFormatterCleanups formatterCleanups = Cleanups.parse( - this.getStringList(JabRefPreferences.CLEANUP_FORMATTERS)); + FieldFormatterCleanups formatterCleanups = Cleanups.parse(getStringList(JabRefPreferences.CLEANUP_FORMATTERS)); return new CleanupPreset(activeJobs, formatterCleanups); } public void setCleanupPreset(CleanupPreset cleanupPreset) { - this.putBoolean(JabRefPreferences.CLEANUP_DOI, cleanupPreset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_DOI)); - this.putBoolean(JabRefPreferences.CLEANUP_ISSN, cleanupPreset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_ISSN)); - this.putBoolean(JabRefPreferences.CLEANUP_MOVE_PDF, cleanupPreset.isActive(CleanupPreset.CleanupStep.MOVE_PDF)); - this.putBoolean(JabRefPreferences.CLEANUP_MAKE_PATHS_RELATIVE, cleanupPreset.isActive(CleanupPreset.CleanupStep.MAKE_PATHS_RELATIVE)); - this.putBoolean(JabRefPreferences.CLEANUP_RENAME_PDF, cleanupPreset.isActive(CleanupPreset.CleanupStep.RENAME_PDF)); - this.putBoolean(JabRefPreferences.CLEANUP_RENAME_PDF_ONLY_RELATIVE_PATHS, - cleanupPreset.isActive(CleanupPreset.CleanupStep.RENAME_PDF_ONLY_RELATIVE_PATHS)); - this.putBoolean(JabRefPreferences.CLEANUP_UPGRADE_EXTERNAL_LINKS, - cleanupPreset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS)); - this.putBoolean(JabRefPreferences.CLEANUP_CONVERT_TO_BIBLATEX, cleanupPreset.isActive(CleanupPreset.CleanupStep.CONVERT_TO_BIBLATEX)); - this.putBoolean(JabRefPreferences.CLEANUP_CONVERT_TO_BIBTEX, cleanupPreset.isActive(CleanupPreset.CleanupStep.CONVERT_TO_BIBTEX)); - this.putBoolean(JabRefPreferences.CLEANUP_FIX_FILE_LINKS, cleanupPreset.isActive(CleanupPreset.CleanupStep.FIX_FILE_LINKS)); - this.putStringList(JabRefPreferences.CLEANUP_FORMATTERS, cleanupPreset.getFormatterCleanups().getAsStringList(OS.NEWLINE)); + for (CleanupPreset.CleanupStep action : EnumSet.allOf(CleanupPreset.CleanupStep.class)) { + putBoolean(JabRefPreferences.CLEANUP + action.name(), cleanupPreset.isActive(action)); + } + + putStringList(JabRefPreferences.CLEANUP_FORMATTERS, cleanupPreset.getFormatterCleanups().getAsStringList(OS.NEWLINE)); } public RemotePreferences getRemotePreferences() { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index f7b09efbf18..46918dfc565 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2229,7 +2229,7 @@ Keystore\:=Keystore\: Password\:=Password\: Remember\ Password=Remember Password Use\ SSL=Use SSL - +Move\ preprint\ information\ from\ 'URL'\ and\ 'journal'\ field\ to\ the\ 'eprint'\ field=Move preprint information from 'URL' and 'journal' field to the 'eprint' field Default\ drag\ &\ drop\ action=Default drag & drop action Copy\ file\ to\ default\ file\ folder=Copy file to default file folder Link\ file\ (without\ copying)=Link file (without copying) diff --git a/src/test/java/org/jabref/logic/cleanup/EprintCleanupTest.java b/src/test/java/org/jabref/logic/cleanup/EprintCleanupTest.java new file mode 100644 index 00000000000..2ad3bebfb36 --- /dev/null +++ b/src/test/java/org/jabref/logic/cleanup/EprintCleanupTest.java @@ -0,0 +1,29 @@ +package org.jabref.logic.cleanup; + +import org.jabref.model.entry.BibEntry; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class EprintCleanupTest { + + @Test + void cleanupCompleteEntry() { + BibEntry input = new BibEntry() + .withField("journaltitle", "arXiv:1502.05795 [math]") + .withField("note", "arXiv: 1502.05795") + .withField("url", "http://arxiv.org/abs/1502.05795") + .withField("urldate", "2018-09-07TZ"); + + BibEntry expected = new BibEntry() + .withField("eprint", "1502.05795") + .withField("eprintclass", "math") + .withField("eprinttype", "arxiv"); + + EprintCleanup cleanup = new EprintCleanup(); + cleanup.cleanup(input); + + assertEquals(expected, input); + } +} diff --git a/src/test/java/org/jabref/model/entry/identifier/ArXivIdentifierTest.java b/src/test/java/org/jabref/model/entry/identifier/ArXivIdentifierTest.java index 3109f4b3c76..0c9250c5529 100644 --- a/src/test/java/org/jabref/model/entry/identifier/ArXivIdentifierTest.java +++ b/src/test/java/org/jabref/model/entry/identifier/ArXivIdentifierTest.java @@ -6,19 +6,61 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class ArXivIdentifierTest { +class ArXivIdentifierTest { @Test - public void parseIgnoresArXivPrefix() throws Exception { + void parse() throws Exception { + Optional parsed = ArXivIdentifier.parse("0710.0994"); + + assertEquals(Optional.of(new ArXivIdentifier("0710.0994")), parsed); + } + + @Test + void parseWithArXivPrefix() throws Exception { Optional parsed = ArXivIdentifier.parse("arXiv:0710.0994"); assertEquals(Optional.of(new ArXivIdentifier("0710.0994")), parsed); } @Test - public void parseIgnoresArxivPrefix() throws Exception { + void parseWithArxivPrefix() throws Exception { Optional parsed = ArXivIdentifier.parse("arxiv:0710.0994"); assertEquals(Optional.of(new ArXivIdentifier("0710.0994")), parsed); } + + @Test + void parseWithClassification() throws Exception { + Optional parsed = ArXivIdentifier.parse("0706.0001v1 [q-bio.CB]"); + + assertEquals(Optional.of(new ArXivIdentifier("0706.0001v1", "q-bio.CB")), parsed); + } + + @Test + void parseWithArXivPrefixAndClassification() throws Exception { + Optional parsed = ArXivIdentifier.parse("arXiv:0706.0001v1 [q-bio.CB]"); + + assertEquals(Optional.of(new ArXivIdentifier("0706.0001v1", "q-bio.CB")), parsed); + } + + @Test + void parseOldIdentifier() throws Exception { + Optional parsed = ArXivIdentifier.parse("math.GT/0309136"); + + assertEquals(Optional.of(new ArXivIdentifier("math.GT/0309136", "math.GT")), parsed); + } + + @Test + void parseOldIdentifierWithArXivPrefix() throws Exception { + Optional parsed = ArXivIdentifier.parse("arXiv:math.GT/0309136"); + + assertEquals(Optional.of(new ArXivIdentifier("math.GT/0309136", "math.GT")), parsed); + } + + @Test + void parseUrl() throws Exception { + Optional parsed = ArXivIdentifier.parse("http://arxiv.org/abs/1502.05795"); + + assertEquals(Optional.of(new ArXivIdentifier("1502.05795", "")), parsed); + } }