From 065523d84805618c1686e893d1998ea5ce9ca2c7 Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 3 Nov 2018 17:11:30 +0100 Subject: [PATCH 1/5] New translations JabRef_en.properties (German) (#4455) --- src/main/resources/l10n/JabRef_de.properties | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index f266b945a78..74cc9c60665 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -32,6 +32,7 @@ Accept=Übernehmen Accept\ change=Änderung akzeptieren +Accept\ recommendations\ from\ Mr.\ DLib=Empfehlungen von Mr. DLib akzeptieren Action=Aktion @@ -159,6 +160,7 @@ Clear\ fields=Felder löschen Close=Schließen +Close\ entry=Eintrag schließen Close\ dialog=Dialog schließen @@ -215,6 +217,7 @@ Could\ not\ run\ the\ 'vim'\ program.=Das Programm 'vim' konnte nicht gestartet Could\ not\ save\ file.=Datei konnte nicht gespeichert werden. Character\ encoding\ '%0'\ is\ not\ supported.=Die Zeichenkodierung '%0' wird nicht unterstützt. +Create\ custom\ fields\ for\ each\ BibTeX\ entry=Erstellen Sie benutzerdefinierte Felder für jeden BibTeX-Eintrag crossreferenced\ entries\ included=Inklusive querverwiesenen Einträgen @@ -325,6 +328,7 @@ Dynamically\ group\ entries\ by\ a\ free-form\ search\ expression=Dynamisches Gr Dynamically\ group\ entries\ by\ searching\ a\ field\ for\ a\ keyword=Dynamisches Gruppieren der Einträge anhand eines Stichworts in einem Feld +Each\ line\ must\ be\ of\ the\ following\ form=Jede Zeile muss das folgende Format aufweisen Edit=Bearbeiten @@ -376,6 +380,7 @@ Error\ occurred\ when\ parsing\ entry=Fehler beim Analysieren des Eintrags Error\ opening\ file=Fehler beim Öffnen der Datei +Error\ while\ fetching\ from\ Mr.DLib.=Fehler beim Abrufen von Mr.DLib. Error\ while\ writing=Fehler beim Schreiben @@ -452,6 +457,7 @@ Float=Oben einsortieren for=für +Format\:\ Tab\:field;field;...\ (e.g.\ General\:url;pdf;note...)=Format\: Tab\:Feld;Feld\:... (z. B. General\:url;pdf\:note...) Format\ of\ author\ and\ editor\ names=Format der Autoren- und Hrsg.-Namen Format\ string=Formatier-Ausdruck @@ -463,9 +469,11 @@ found\ in\ AUX\ file=gefundene Schlüssel in AUX Datei Full\ name=Kompletter Name +Further\ information\ about\ Mr\ DLib.\ for\ JabRef\ users.=Weitere Informationen über Mr. DLib für JabRef Benutzer. General=Allgemein +General\ Fields=Allgemeine Felder Generate=Erzeugen @@ -557,6 +565,7 @@ Independent\ group\:\ When\ selected,\ view\ only\ this\ group's\ entries=Unabh Work\ options=Bearbeitungsoptionen +I\ Agree=Ich stimme zu Insert=einfügen @@ -654,6 +663,7 @@ Move\ up=Nach oben Moved\ group\ "%0".=Gruppe "%0" verschoben. +Mr.\ DLib\ is\ an\ external\ service\ which\ provides\ article\ recommendations\ based\ on\ the\ currently\ selected\ entry.\ Data\ about\ the\ selected\ entry\ must\ be\ sent\ to\ Mr.\ DLib\ in\ order\ to\ provide\ these\ recommendations.\ Do\ you\ agree\ that\ this\ data\ may\ be\ sent?=Mr. DLib ist ein externer Service der Artikelempfehlungen basierend auf den aktuell ausgewählten Eintrag anbietet. Daten über den ausgewählten Eintrag müssen an Mr. DLib gesendet werden, um diese Empfehlungen anzubieten. Sind Sie einverstanden, dass diese Daten gesendet werden dürfen? Name=Name @@ -786,6 +796,7 @@ Please\ enter\ the\ string's\ label=Geben Sie bitte den Namen des Strings ein. Please\ select\ an\ importer.=Bitte Importer auswählen. +Please\ restart\ JabRef\ for\ preferences\ to\ take\ effect.=Bitte starten Sie die Anwendung neu, damit die Änderungen wirksam werden. Possible\ duplicate\ entries=Mögliche doppelte Einträge @@ -875,6 +886,9 @@ Removed\ string=String gelöscht Renamed\ string=String umbenannt Replace=Ersetzen +Replace\ With\:=Ersetzen durch\: +Limit\ to\ Selected\ Entries=Auf ausgewählten Einträge begrenzen +Limit\ to\ Fields=Auf folgende Felder begrenzen Replace\ (regular\ expression)=Ersetzen (regulärer Ausdruck) From 185c47a7a629ad7c33d1728f5d16c377cba8ac03 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 4 Nov 2018 17:00:38 +0100 Subject: [PATCH 2/5] Improve MathSciNet fetcher and add ISBN fetcher to entry editor toolbar (#4444) * Improve MathSciNet fetcher and add ISBN fetcher to entry editor toolbar Makes the MathSciNet fetcher more reliable in the case the entry already contains a mrnumber. Moreover, adds the ISBN fetcher to the list of fetcher available under "Update with bibliographic information from the web" in the entry editor toolbar. * Fix checkstyle Co-authored-by: Tobias Diez * Fix checkstyle Co-authored-by: Tobias Diez --- CHANGELOG.md | 2 + .../jabref/logic/importer/WebFetchers.java | 1 + .../logic/importer/fetcher/IsbnFetcher.java | 38 +++++++++++-------- .../logic/importer/fetcher/MathSciNet.java | 7 ++++ .../importer/fetcher/IsbnFetcherTest.java | 32 ++++++++++------ .../importer/fetcher/MathSciNetTest.java | 13 ++++++- 6 files changed, 65 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2119c71557..0d5069e2517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We changed the default keyboard shortcuts for moving between entries when the entry editor is active to ̀alt + up/down. - Opening a new file now prompts the directory of the currently selected file, instead of the directory of the last opened file. - Window state is saved on close and restored on start. +- We made the MathSciNet fetcher more reliable. +- We added the ISBN fetcher to the list of fetcher available under "Update with bibliographic information from the web" in the entry editor toolbar. - Files without a defined external file type are now directly opened with the default application of the operating system - We streamlined the process to rename and move files by removing the confirmation dialogs. - We removed the redundant new lines of markings and wrapped the summary in the File annotation tab. [#3823](https://github.com/JabRef/jabref/issues/3823) diff --git a/src/main/java/org/jabref/logic/importer/WebFetchers.java b/src/main/java/org/jabref/logic/importer/WebFetchers.java index 3329a6215f2..c06849201f3 100644 --- a/src/main/java/org/jabref/logic/importer/WebFetchers.java +++ b/src/main/java/org/jabref/logic/importer/WebFetchers.java @@ -119,6 +119,7 @@ public static List getEntryBasedFetchers(ImportFormatPreferen ArrayList list = new ArrayList<>(); list.add(new AstrophysicsDataSystem(importFormatPreferences)); list.add(new DoiFetcher(importFormatPreferences)); + list.add(new IsbnFetcher(importFormatPreferences)); list.add(new MathSciNet(importFormatPreferences)); list.add(new CrossRef()); list.sort(Comparator.comparing(WebFetcher::getName)); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java index 343d5e3e8cd..c1d13f862e9 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/IsbnFetcher.java @@ -1,23 +1,32 @@ package org.jabref.logic.importer.fetcher; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; +import java.util.Collections; +import java.util.List; import java.util.Optional; +import org.jabref.logic.help.HelpFile; +import org.jabref.logic.importer.EntryBasedFetcher; import org.jabref.logic.importer.FetcherException; +import org.jabref.logic.importer.IdBasedFetcher; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.FieldName; +import org.jabref.model.util.OptionalUtil; import org.jsoup.helper.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Fetcher for ISBN trying ebook.de first and then chimbori.com */ -public class IsbnFetcher extends AbstractIsbnFetcher { +public class IsbnFetcher implements EntryBasedFetcher, IdBasedFetcher { + + private static final Logger LOGGER = LoggerFactory.getLogger(IsbnFetcher.class); + protected final ImportFormatPreferences importFormatPreferences; public IsbnFetcher(ImportFormatPreferences importFormatPreferences) { - super(importFormatPreferences); + this.importFormatPreferences = importFormatPreferences; } @Override @@ -25,12 +34,9 @@ public String getName() { return "ISBN"; } - /** - * Method never used - */ @Override - public URL getURLForID(String identifier) throws URISyntaxException, MalformedURLException, FetcherException { - return null; + public Optional getHelpPage() { + return Optional.of(HelpFile.FETCHER_ISBN); } @Override @@ -39,8 +45,6 @@ public Optional performSearchById(String identifier) throws FetcherExc return Optional.empty(); } - this.ensureThatIsbnIsValid(identifier); - IsbnViaEbookDeFetcher isbnViaEbookDeFetcher = new IsbnViaEbookDeFetcher(importFormatPreferences); Optional bibEntry = isbnViaEbookDeFetcher.performSearchById(identifier); // nothing found at ebook.de, try chimbori.com @@ -55,8 +59,12 @@ public Optional performSearchById(String identifier) throws FetcherExc } @Override - public void doPostCleanup(BibEntry entry) { - // no action needed + public List performSearch(BibEntry entry) throws FetcherException { + Optional isbn = entry.getField(FieldName.ISBN); + if (isbn.isPresent()) { + return OptionalUtil.toList(performSearchById(isbn.get())); + } else { + return Collections.emptyList(); + } } - } diff --git a/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java b/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java index 603e84eac60..774d4155c00 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/MathSciNet.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -52,6 +53,12 @@ public String getName() { */ @Override public URL getURLForEntry(BibEntry entry) throws URISyntaxException, MalformedURLException, FetcherException { + Optional mrNumberInEntry = entry.getField(FieldName.MR_NUMBER); + if (mrNumberInEntry.isPresent()) { + // We are lucky and already know the id, so use it instead + return getURLForID(mrNumberInEntry.get()); + } + URIBuilder uriBuilder = new URIBuilder("https://mathscinet.ams.org/mrlookup"); uriBuilder.addParameter("format", "bibtex"); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/IsbnFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/IsbnFetcherTest.java index ed140ff3dc8..b6b34971a17 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/IsbnFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/IsbnFetcherTest.java @@ -1,5 +1,7 @@ package org.jabref.logic.importer.fetcher; +import java.util.Collections; +import java.util.List; import java.util.Optional; import org.jabref.logic.importer.FetcherException; @@ -18,13 +20,13 @@ import static org.mockito.Mockito.mock; @FetcherTest -public class IsbnFetcherTest { +class IsbnFetcherTest { private IsbnFetcher fetcher; private BibEntry bibEntry; @BeforeEach - public void setUp() { + void setUp() { fetcher = new IsbnFetcher(mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS)); bibEntry = new BibEntry(); @@ -41,54 +43,62 @@ public void setUp() { } @Test - public void testName() { + void testName() { assertEquals("ISBN", fetcher.getName()); } @Test - public void testHelpPage() { + void testHelpPage() { assertEquals("ISBNtoBibTeX", fetcher.getHelpPage().get().getPageName()); } @Test - public void searchByIdSuccessfulWithShortISBN() throws FetcherException { + void searchByIdSuccessfulWithShortISBN() throws FetcherException { Optional fetchedEntry = fetcher.performSearchById("0134685997"); assertEquals(Optional.of(bibEntry), fetchedEntry); } @Test - public void searchByIdSuccessfulWithLongISBN() throws FetcherException { + void searchByIdSuccessfulWithLongISBN() throws FetcherException { Optional fetchedEntry = fetcher.performSearchById("9780134685991"); assertEquals(Optional.of(bibEntry), fetchedEntry); } @Test - public void searchByIdReturnsEmptyWithEmptyISBN() throws FetcherException { + void searchByIdReturnsEmptyWithEmptyISBN() throws FetcherException { Optional fetchedEntry = fetcher.performSearchById(""); assertEquals(Optional.empty(), fetchedEntry); } @Test - public void searchByIdThrowsExceptionForShortInvalidISBN() { + void searchByIdThrowsExceptionForShortInvalidISBN() { assertThrows(FetcherException.class, () -> fetcher.performSearchById("123456789")); } @Test - public void searchByIdThrowsExceptionForLongInvalidISB() { + void searchByIdThrowsExceptionForLongInvalidISB() { assertThrows(FetcherException.class, () -> fetcher.performSearchById("012345678910")); } @Test - public void searchByIdThrowsExceptionForInvalidISBN() { + void searchByIdThrowsExceptionForInvalidISBN() { assertThrows(FetcherException.class, () -> fetcher.performSearchById("jabref-4-ever")); } + @Test + void searchByEntryWithISBNSuccessful() throws FetcherException { + BibEntry input = new BibEntry().withField("isbn", "0134685997"); + + List fetchedEntry = fetcher.performSearch(input); + assertEquals(Collections.singletonList(bibEntry), fetchedEntry); + } + /** * This test searches for a valid ISBN. See https://www.amazon.de/dp/3728128155/?tag=jabref-21 However, this ISBN is * not available on ebook.de. The fetcher should something as it falls back to Chimbori */ @Test - public void searchForIsbnAvailableAtChimboriButNonOnEbookDe() throws FetcherException { + void searchForIsbnAvailableAtChimboriButNonOnEbookDe() throws FetcherException { Optional fetchedEntry = fetcher.performSearchById("3728128155"); assertNotEquals(Optional.empty(), fetchedEntry); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/MathSciNetTest.java b/src/test/java/org/jabref/logic/importer/fetcher/MathSciNetTest.java index e66d81f0948..cf938c036bb 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/MathSciNetTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/MathSciNetTest.java @@ -1,5 +1,6 @@ package org.jabref.logic.importer.fetcher; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -55,8 +56,16 @@ void searchByEntryFindsEntry() throws Exception { searchEntry.setField("journal", "fluid"); List fetchedEntries = fetcher.performSearch(searchEntry); - assertFalse(fetchedEntries.isEmpty()); - assertEquals(ratiuEntry, fetchedEntries.get(0)); + assertEquals(Collections.singletonList(ratiuEntry), fetchedEntries); + } + + @Test + void searchByIdInEntryFindsEntry() throws Exception { + BibEntry searchEntry = new BibEntry(); + searchEntry.setField("mrnumber", "3537908"); + + List fetchedEntries = fetcher.performSearch(searchEntry); + assertEquals(Collections.singletonList(ratiuEntry), fetchedEntries); } @Test From 5548f8f04eb257d148292b6fedff6c2c86518237 Mon Sep 17 00:00:00 2001 From: Stephen Beitzel Date: Tue, 6 Nov 2018 13:12:52 -0800 Subject: [PATCH 3/5] Set auto-update checkbox enable/disable when reading preferences (#4446) * When initializing the UI from prefs, fire an event to make sure dependent controls get updated. Drive-by code cleanup -- enums are singletons; don't call enum.equals(), just use ==. * Update changelog to document fix. * Refactor enable/disable logic to its own method * Revert CHANGELOG. Change checkbox disable/enable logic to use FX bind / observable. --- .../java/org/jabref/gui/preferences/GeneralTab.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/gui/preferences/GeneralTab.java b/src/main/java/org/jabref/gui/preferences/GeneralTab.java index 2cb74aca865..8e065aab85c 100644 --- a/src/main/java/org/jabref/gui/preferences/GeneralTab.java +++ b/src/main/java/org/jabref/gui/preferences/GeneralTab.java @@ -26,6 +26,8 @@ import org.jabref.model.entry.InternalBibtexFields; import org.jabref.preferences.JabRefPreferences; +import static javafx.beans.binding.Bindings.not; + class GeneralTab extends Pane implements PrefsTab { private final CheckBox useOwner; @@ -59,10 +61,7 @@ public GeneralTab(DialogService dialogService, JabRefPreferences prefs) { updateTimeStamp = new CheckBox(Localization.lang("Update timestamp on modification")); useTimeStamp = new CheckBox(Localization.lang("Mark new entries with addition date") + ". " + Localization.lang("Date format") + ':'); - if (!useTimeStamp.isSelected()) { - updateTimeStamp.setDisable(true); - } - useTimeStamp.setOnAction(e->updateTimeStamp.setDisable(!useTimeStamp.isSelected())); + updateTimeStamp.disableProperty().bind(not(useTimeStamp.selectedProperty())); overwriteOwner = new CheckBox(Localization.lang("Overwrite")); overwriteTimeStamp = new CheckBox(Localization.lang("If a pasted or imported entry already has the field set, overwrite.")); enforceLegalKeys = new CheckBox(Localization.lang("Enforce legal characters in BibTeX keys")); @@ -182,9 +181,9 @@ public void storeSettings() { // Update name of the time stamp field based on preferences InternalBibtexFields.updateTimeStampField(prefs.get(JabRefPreferences.TIME_STAMP_FIELD)); prefs.setDefaultEncoding(encodings.getValue()); - prefs.putBoolean(JabRefPreferences.BIBLATEX_DEFAULT_MODE, biblatexMode.getValue().equals(BibDatabaseMode.BIBLATEX)); + prefs.putBoolean(JabRefPreferences.BIBLATEX_DEFAULT_MODE, biblatexMode.getValue() == BibDatabaseMode.BIBLATEX); - if (!languageSelection.getValue().equals(prefs.getLanguage())) { + if (languageSelection.getValue() != prefs.getLanguage()) { prefs.setLanguage(languageSelection.getValue()); Localization.setLanguage(languageSelection.getValue()); From 57281d279a258b49456183d641ea1cfadd6c006c Mon Sep 17 00:00:00 2001 From: Stephen Beitzel Date: Wed, 7 Nov 2018 11:37:22 -0800 Subject: [PATCH 4/5] respect preference state (#4468) * When initializing the UI from prefs, fire an event to make sure dependent controls get updated. Drive-by code cleanup -- enums are singletons; don't call enum.equals(), just use ==. * Update changelog to document fix. * Refactor enable/disable logic to its own method * Revert CHANGELOG. Change checkbox disable/enable logic to use FX bind / observable. * remove line which overrides the preference for updateTimeStamp --- src/main/java/org/jabref/gui/preferences/GeneralTab.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/preferences/GeneralTab.java b/src/main/java/org/jabref/gui/preferences/GeneralTab.java index 8e065aab85c..d2c27c1a97c 100644 --- a/src/main/java/org/jabref/gui/preferences/GeneralTab.java +++ b/src/main/java/org/jabref/gui/preferences/GeneralTab.java @@ -139,7 +139,6 @@ public void setValues() { useTimeStamp.setSelected(prefs.getBoolean(JabRefPreferences.USE_TIME_STAMP)); overwriteTimeStamp.setSelected(prefs.getBoolean(JabRefPreferences.OVERWRITE_TIME_STAMP)); updateTimeStamp.setSelected(prefs.getBoolean(JabRefPreferences.UPDATE_TIMESTAMP)); - updateTimeStamp.setSelected(useTimeStamp.isSelected()); enforceLegalKeys.setSelected(prefs.getBoolean(JabRefPreferences.ENFORCE_LEGAL_BIBTEX_KEY)); shouldCollectTelemetry.setSelected(prefs.shouldCollectTelemetry()); memoryStick.setSelected(prefs.getBoolean(JabRefPreferences.MEMORY_STICK_MODE)); From 144522d61aa286d4604f7ea41c6869786f1161e4 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 7 Nov 2018 21:17:49 +0100 Subject: [PATCH 5/5] Add eprint cleanup (#4445) * Adds a cleanup operation that detects an arXiv identifier in the note, journal or url field and moves it to the `eprint` field. I also took the opportunity to convert the cleanup panel to FXML and somewhat simplified/refactored the code around the cleanup. * Implement feedback * Fix cleanup --- CHANGELOG.md | 2 + .../org/jabref/gui/actions/CleanupAction.java | 49 +++---- .../org/jabref/gui/cleanup/CleanupDialog.java | 5 +- .../gui/cleanup/CleanupPresetPanel.fxml | 33 +++++ .../gui/cleanup/CleanupPresetPanel.java | 138 ++++++++---------- .../cleanup/FieldFormatterCleanupsPanel.java | 2 +- .../jabref/logic/cleanup/CleanupPreset.java | 42 +----- .../jabref/logic/cleanup/CleanupWorker.java | 59 ++++---- .../org/jabref/logic/cleanup/DoiCleanup.java | 11 +- .../jabref/logic/cleanup/EprintCleanup.java | 52 +++++++ .../entry/identifier/ArXivIdentifier.java | 71 +++++++-- .../jabref/preferences/JabRefPreferences.java | 94 +++--------- src/main/resources/l10n/JabRef_en.properties | 2 +- .../logic/cleanup/EprintCleanupTest.java | 29 ++++ .../entry/identifier/ArXivIdentifierTest.java | 48 +++++- 15 files changed, 367 insertions(+), 270 deletions(-) create mode 100644 src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml create mode 100644 src/main/java/org/jabref/logic/cleanup/EprintCleanup.java create mode 100644 src/test/java/org/jabref/logic/cleanup/EprintCleanupTest.java 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); + } }