diff --git a/CHANGELOG.md b/CHANGELOG.md index e536e03ef78..5d271da2053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We added support for basic markdown in custom formatted previews [#6194](https://github.com/JabRef/jabref/issues/6194) - We now show the number of items found and selected to import in the online search dialog. [#6248](https://github.com/JabRef/jabref/pull/6248) - We created a new install screen for macOS. [#5759](https://github.com/JabRef/jabref/issues/5759) +- We added a new integrity check for duplicate DOIs. [koppor#339](https://github.com/koppor/jabref/issues/339) - We implemented an option to download fulltext files while importing. [#6381](https://github.com/JabRef/jabref/pull/6381) - We added a progress-indicator showing the average progress of background tasks to the toolbar. Clicking it reveals a pop-over with a list of running background tasks. [6443](https://github.com/JabRef/jabref/pull/6443) - We fixed the bug when strike the delete key in the text field. [#6421](https://github.com/JabRef/jabref/issues/6421) diff --git a/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java b/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java index 9d27b810daf..f2b60244974 100644 --- a/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java +++ b/src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java @@ -53,6 +53,7 @@ protected List call() { List result = new ArrayList<>(); ObservableList entries = database.getDatabase().getEntries(); + result.addAll(check.checkDatabase(database.getDatabase())); for (int i = 0; i < entries.size(); i++) { if (isCancelled()) { break; diff --git a/src/main/java/org/jabref/logic/integrity/ASCIICharacterChecker.java b/src/main/java/org/jabref/logic/integrity/ASCIICharacterChecker.java index 34988fe7495..4072b35b8b9 100644 --- a/src/main/java/org/jabref/logic/integrity/ASCIICharacterChecker.java +++ b/src/main/java/org/jabref/logic/integrity/ASCIICharacterChecker.java @@ -4,14 +4,13 @@ import java.util.List; import java.util.Map; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import com.google.common.base.CharMatcher; -public class ASCIICharacterChecker implements Checker { +public class ASCIICharacterChecker implements EntryChecker { /** * Detect any non ASCII encoded characters, e.g., umlauts or unicode in the fields diff --git a/src/main/java/org/jabref/logic/integrity/BibStringChecker.java b/src/main/java/org/jabref/logic/integrity/BibStringChecker.java index 0e46c15e440..acae104f885 100644 --- a/src/main/java/org/jabref/logic/integrity/BibStringChecker.java +++ b/src/main/java/org/jabref/logic/integrity/BibStringChecker.java @@ -6,20 +6,19 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldProperty; -public class BibStringChecker implements Checker { +/** + * Checks, if there is an even number of unescaped # + */ +public class BibStringChecker implements EntryChecker { // Detect # if it doesn't have a \ in front of it or if it starts the string private static final Pattern UNESCAPED_HASH = Pattern.compile("(? check(BibEntry entry) { List results = new ArrayList<>(); diff --git a/src/main/java/org/jabref/logic/integrity/BibTeXEntryTypeChecker.java b/src/main/java/org/jabref/logic/integrity/BibTeXEntryTypeChecker.java index 6d465292fd2..b83e3039e93 100644 --- a/src/main/java/org/jabref/logic/integrity/BibTeXEntryTypeChecker.java +++ b/src/main/java/org/jabref/logic/integrity/BibTeXEntryTypeChecker.java @@ -3,7 +3,6 @@ import java.util.Collections; import java.util.List; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.InternalField; @@ -12,7 +11,7 @@ /** * BibTeX mode only checker */ -public class BibTeXEntryTypeChecker implements Checker { +public class BibTeXEntryTypeChecker implements EntryChecker { /** * Will check if the current library uses any entry types from another mode. * For example it will warn the user if he uses entry types defined for Biblatex inside a BibTeX library. diff --git a/src/main/java/org/jabref/logic/integrity/BibtexKeyChecker.java b/src/main/java/org/jabref/logic/integrity/BibtexKeyChecker.java index 91290465a24..e3799b2e979 100644 --- a/src/main/java/org/jabref/logic/integrity/BibtexKeyChecker.java +++ b/src/main/java/org/jabref/logic/integrity/BibtexKeyChecker.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Optional; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.InternalField; @@ -14,14 +13,14 @@ /** * Currently only checks the key if there is an author, year, and title present. */ -public class BibtexKeyChecker implements Checker { +public class BibtexKeyChecker implements EntryChecker { @Override public List check(BibEntry entry) { Optional author = entry.getField(StandardField.AUTHOR); Optional title = entry.getField(StandardField.TITLE); Optional year = entry.getField(StandardField.YEAR); - if (!author.isPresent() || !title.isPresent() || !year.isPresent()) { + if (author.isEmpty() || title.isEmpty() || year.isEmpty()) { return Collections.emptyList(); } diff --git a/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java b/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java index 7112653709e..7048fc51409 100644 --- a/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java +++ b/src/main/java/org/jabref/logic/integrity/BibtexKeyDuplicationChecker.java @@ -5,13 +5,12 @@ import java.util.Objects; import java.util.Optional; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; -public class BibtexKeyDuplicationChecker implements Checker { +public class BibtexKeyDuplicationChecker implements EntryChecker { private final BibDatabase database; diff --git a/src/main/java/org/jabref/logic/integrity/BibtexkeyDeviationChecker.java b/src/main/java/org/jabref/logic/integrity/BibtexkeyDeviationChecker.java index 07ded3eb9a7..63098089203 100644 --- a/src/main/java/org/jabref/logic/integrity/BibtexkeyDeviationChecker.java +++ b/src/main/java/org/jabref/logic/integrity/BibtexkeyDeviationChecker.java @@ -7,13 +7,12 @@ import org.jabref.logic.bibtexkeypattern.BibtexKeyGenerator; import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternPreferences; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.InternalField; -public class BibtexkeyDeviationChecker implements Checker { +public class BibtexkeyDeviationChecker implements EntryChecker { private final BibDatabaseContext bibDatabaseContext; private final BibtexKeyPatternPreferences bibtexKeyPatternPreferences; @@ -26,7 +25,7 @@ public BibtexkeyDeviationChecker(BibDatabaseContext bibDatabaseContext, BibtexKe @Override public List check(BibEntry entry) { Optional valuekey = entry.getCiteKeyOptional(); - if (!valuekey.isPresent()) { + if (valuekey.isEmpty()) { return Collections.emptyList(); } diff --git a/src/main/java/org/jabref/logic/integrity/DatabaseChecker.java b/src/main/java/org/jabref/logic/integrity/DatabaseChecker.java new file mode 100644 index 00000000000..0f03cccce63 --- /dev/null +++ b/src/main/java/org/jabref/logic/integrity/DatabaseChecker.java @@ -0,0 +1,10 @@ +package org.jabref.logic.integrity; + +import java.util.List; + +import org.jabref.model.database.BibDatabase; + +@FunctionalInterface +public interface DatabaseChecker { + List check(BibDatabase database); +} diff --git a/src/main/java/org/jabref/logic/integrity/DoiDuplicationChecker.java b/src/main/java/org/jabref/logic/integrity/DoiDuplicationChecker.java new file mode 100644 index 00000000000..41dfcdfba1a --- /dev/null +++ b/src/main/java/org/jabref/logic/integrity/DoiDuplicationChecker.java @@ -0,0 +1,35 @@ +package org.jabref.logic.integrity; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javafx.collections.ObservableList; + +import org.jabref.logic.l10n.Localization; +import org.jabref.model.database.BibDatabase; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.identifier.DOI; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +public class DoiDuplicationChecker implements DatabaseChecker { + + @Override + public List check(BibDatabase database) { + ObservableList bibEntries = database.getEntries(); + BiMap> duplicateMap = HashBiMap.create(bibEntries.size()); + for (BibEntry bibEntry : bibEntries) { + bibEntry.getDOI().ifPresent(doi -> + duplicateMap.computeIfAbsent(doi, absentDoi -> new ArrayList<>()).add(bibEntry)); + } + + return duplicateMap.inverse().keySet().stream() + .filter(list -> list.size() > 1) + .flatMap(list -> list.stream()) + .map(item -> new IntegrityMessage(Localization.lang("Same DOI used in multiple entries"), item, StandardField.DOI)) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/jabref/logic/integrity/DOIValidityChecker.java b/src/main/java/org/jabref/logic/integrity/DoiValidityChecker.java similarity index 90% rename from src/main/java/org/jabref/logic/integrity/DOIValidityChecker.java rename to src/main/java/org/jabref/logic/integrity/DoiValidityChecker.java index 09c144a087f..a78c8b4d0ea 100644 --- a/src/main/java/org/jabref/logic/integrity/DOIValidityChecker.java +++ b/src/main/java/org/jabref/logic/integrity/DoiValidityChecker.java @@ -6,8 +6,7 @@ import org.jabref.model.entry.identifier.DOI; import org.jabref.model.strings.StringUtil; -public class DOIValidityChecker implements ValueChecker { - +public class DoiValidityChecker implements ValueChecker { @Override public Optional checkValue(String value) { if (StringUtil.isBlank(value)) { diff --git a/src/main/java/org/jabref/logic/integrity/EntryChecker.java b/src/main/java/org/jabref/logic/integrity/EntryChecker.java new file mode 100644 index 00000000000..bcbe0e79769 --- /dev/null +++ b/src/main/java/org/jabref/logic/integrity/EntryChecker.java @@ -0,0 +1,10 @@ +package org.jabref.logic.integrity; + +import java.util.List; + +import org.jabref.model.entry.BibEntry; + +@FunctionalInterface +public interface EntryChecker { + List check(BibEntry entry); +} diff --git a/src/main/java/org/jabref/logic/integrity/EntryLinkChecker.java b/src/main/java/org/jabref/logic/integrity/EntryLinkChecker.java index be2c00258fa..7df582d309c 100644 --- a/src/main/java/org/jabref/logic/integrity/EntryLinkChecker.java +++ b/src/main/java/org/jabref/logic/integrity/EntryLinkChecker.java @@ -7,14 +7,13 @@ import java.util.Objects; import java.util.Set; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldProperty; -public class EntryLinkChecker implements Checker { +public class EntryLinkChecker implements EntryChecker { private final BibDatabase database; @@ -28,14 +27,14 @@ public List check(BibEntry entry) { for (Entry field : entry.getFieldMap().entrySet()) { Set properties = field.getKey().getProperties(); if (properties.contains(FieldProperty.SINGLE_ENTRY_LINK)) { - if (!database.getEntryByKey(field.getValue()).isPresent()) { + if (database.getEntryByKey(field.getValue()).isEmpty()) { result.add(new IntegrityMessage(Localization.lang("Referenced BibTeX key does not exist"), entry, field.getKey())); } } else if (properties.contains(FieldProperty.MULTIPLE_ENTRY_LINK)) { List keys = new ArrayList<>(Arrays.asList(field.getValue().split(","))); for (String key : keys) { - if (!database.getEntryByKey(key).isPresent()) { + if (database.getEntryByKey(key).isEmpty()) { result.add(new IntegrityMessage( Localization.lang("Referenced BibTeX key does not exist") + ": " + key, entry, field.getKey())); diff --git a/src/main/java/org/jabref/logic/integrity/FieldChecker.java b/src/main/java/org/jabref/logic/integrity/FieldChecker.java index 9416bdabe42..1e2fb707895 100644 --- a/src/main/java/org/jabref/logic/integrity/FieldChecker.java +++ b/src/main/java/org/jabref/logic/integrity/FieldChecker.java @@ -9,7 +9,7 @@ import org.jabref.model.entry.field.Field; import org.jabref.model.util.OptionalUtil; -public class FieldChecker implements IntegrityCheck.Checker { +public class FieldChecker implements EntryChecker { protected final Field field; private final ValueChecker checker; @@ -21,7 +21,7 @@ public FieldChecker(Field field, ValueChecker checker) { @Override public List check(BibEntry entry) { Optional value = entry.getField(field); - if (!value.isPresent()) { + if (value.isEmpty()) { return Collections.emptyList(); } diff --git a/src/main/java/org/jabref/logic/integrity/FieldCheckers.java b/src/main/java/org/jabref/logic/integrity/FieldCheckers.java index db02399dc73..086b68fe1aa 100644 --- a/src/main/java/org/jabref/logic/integrity/FieldCheckers.java +++ b/src/main/java/org/jabref/logic/integrity/FieldCheckers.java @@ -39,7 +39,7 @@ private static Multimap getAllMap(BibDatabaseContext databa fieldCheckers.put(StandardField.BOOKTITLE, new BooktitleChecker()); fieldCheckers.put(StandardField.TITLE, new BracketChecker()); fieldCheckers.put(StandardField.TITLE, new TitleChecker(databaseContext)); - fieldCheckers.put(StandardField.DOI, new DOIValidityChecker()); + fieldCheckers.put(StandardField.DOI, new DoiValidityChecker()); fieldCheckers.put(StandardField.EDITION, new EditionChecker(databaseContext, allowIntegerEdition)); fieldCheckers.put(StandardField.FILE, new FileChecker(databaseContext, filePreferences)); fieldCheckers.put(StandardField.HOWPUBLISHED, new HowPublishedChecker(databaseContext)); diff --git a/src/main/java/org/jabref/logic/integrity/HTMLCharacterChecker.java b/src/main/java/org/jabref/logic/integrity/HTMLCharacterChecker.java index bf590d654ee..d80e30d3e01 100644 --- a/src/main/java/org/jabref/logic/integrity/HTMLCharacterChecker.java +++ b/src/main/java/org/jabref/logic/integrity/HTMLCharacterChecker.java @@ -6,19 +6,18 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldProperty; -public class HTMLCharacterChecker implements Checker { - // Detect any HTML encoded character, +/** + * Checks, if there are any HTML encoded characters in nonverbatim fields. + */ +public class HTMLCharacterChecker implements EntryChecker { + // Detect any HTML encoded character private static final Pattern HTML_CHARACTER_PATTERN = Pattern.compile("&[#\\p{Alnum}]+;"); - /** - * Checks, if there are any HTML encoded characters in nonverbatim fields. - */ @Override public List check(BibEntry entry) { List results = new ArrayList<>(); diff --git a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java index cb9542b6bae..631c87ba879 100644 --- a/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java +++ b/src/main/java/org/jabref/logic/integrity/IntegrityCheck.java @@ -2,10 +2,10 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternPreferences; import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; @@ -14,71 +14,75 @@ public class IntegrityCheck { private final BibDatabaseContext bibDatabaseContext; - private final FilePreferences filePreferences; - private final BibtexKeyPatternPreferences bibtexKeyPatternPreferences; - private final JournalAbbreviationRepository journalAbbreviationRepository; - private final boolean allowIntegerEdition; + private final FieldCheckers fieldCheckers; + private final List entryCheckers; public IntegrityCheck(BibDatabaseContext bibDatabaseContext, FilePreferences filePreferences, BibtexKeyPatternPreferences bibtexKeyPatternPreferences, JournalAbbreviationRepository journalAbbreviationRepository, boolean allowIntegerEdition) { - this.bibDatabaseContext = Objects.requireNonNull(bibDatabaseContext); - this.filePreferences = Objects.requireNonNull(filePreferences); - this.bibtexKeyPatternPreferences = Objects.requireNonNull(bibtexKeyPatternPreferences); - this.journalAbbreviationRepository = Objects.requireNonNull(journalAbbreviationRepository); - this.allowIntegerEdition = allowIntegerEdition; + this.bibDatabaseContext = bibDatabaseContext; + + fieldCheckers = new FieldCheckers(bibDatabaseContext, + filePreferences, + journalAbbreviationRepository, + allowIntegerEdition); + + entryCheckers = new ArrayList<>(List.of( + new BibtexKeyChecker(), + new TypeChecker(), + new BibStringChecker(), + new HTMLCharacterChecker(), + new EntryLinkChecker(bibDatabaseContext.getDatabase()), + new BibtexkeyDeviationChecker(bibDatabaseContext, bibtexKeyPatternPreferences), + new BibtexKeyDuplicationChecker(bibDatabaseContext.getDatabase()) + )); + + if (bibDatabaseContext.isBiblatexMode()) { + entryCheckers.add(new JournalInAbbreviationListChecker(StandardField.JOURNALTITLE, journalAbbreviationRepository)); + } else { + entryCheckers.addAll(List.of( + new JournalInAbbreviationListChecker(StandardField.JOURNAL, journalAbbreviationRepository), + new ASCIICharacterChecker(), + new NoBibtexFieldChecker(), + new BibTeXEntryTypeChecker()) + ); + } } - public List checkDatabase() { + List check() { List result = new ArrayList<>(); - for (BibEntry entry : bibDatabaseContext.getDatabase().getEntries()) { + BibDatabase database = bibDatabaseContext.getDatabase(); + + for (BibEntry entry : database.getEntries()) { result.addAll(checkEntry(entry)); } + result.addAll(checkDatabase(database)); + return result; } public List checkEntry(BibEntry entry) { List result = new ArrayList<>(); - if (entry == null) { return result; } - FieldCheckers fieldCheckers = new FieldCheckers(bibDatabaseContext, - filePreferences, - journalAbbreviationRepository, - allowIntegerEdition); - for (FieldChecker checker : fieldCheckers.getAll()) { - result.addAll(checker.check(entry)); + for (FieldChecker fieldChecker : fieldCheckers.getAll()) { + result.addAll(fieldChecker.check(entry)); } - if (!bibDatabaseContext.isBiblatexMode()) { - // BibTeX only checkers - result.addAll(new ASCIICharacterChecker().check(entry)); - result.addAll(new NoBibtexFieldChecker().check(entry)); - result.addAll(new BibTeXEntryTypeChecker().check(entry)); - result.addAll(new JournalInAbbreviationListChecker(StandardField.JOURNAL, journalAbbreviationRepository).check(entry)); - } else { - result.addAll(new JournalInAbbreviationListChecker(StandardField.JOURNALTITLE, journalAbbreviationRepository).check(entry)); + for (EntryChecker entryChecker : entryCheckers) { + result.addAll(entryChecker.check(entry)); } - result.addAll(new BibtexKeyChecker().check(entry)); - result.addAll(new TypeChecker().check(entry)); - result.addAll(new BibStringChecker().check(entry)); - result.addAll(new HTMLCharacterChecker().check(entry)); - result.addAll(new EntryLinkChecker(bibDatabaseContext.getDatabase()).check(entry)); - result.addAll(new BibtexkeyDeviationChecker(bibDatabaseContext, bibtexKeyPatternPreferences).check(entry)); - result.addAll(new BibtexKeyDuplicationChecker(bibDatabaseContext.getDatabase()).check(entry)); - return result; } - @FunctionalInterface - public interface Checker { - List check(BibEntry entry); + public List checkDatabase(BibDatabase database) { + return new DoiDuplicationChecker().check(database); } } diff --git a/src/main/java/org/jabref/logic/integrity/IntegrityMessage.java b/src/main/java/org/jabref/logic/integrity/IntegrityMessage.java index 53c0944be0a..94d9d262e4c 100644 --- a/src/main/java/org/jabref/logic/integrity/IntegrityMessage.java +++ b/src/main/java/org/jabref/logic/integrity/IntegrityMessage.java @@ -6,9 +6,7 @@ import org.jabref.model.entry.field.Field; public final class IntegrityMessage implements Cloneable { - private final BibEntry entry; - private final Field field; private final String message; diff --git a/src/main/java/org/jabref/logic/integrity/JournalInAbbreviationListChecker.java b/src/main/java/org/jabref/logic/integrity/JournalInAbbreviationListChecker.java index cfb684537d5..d954debab44 100644 --- a/src/main/java/org/jabref/logic/integrity/JournalInAbbreviationListChecker.java +++ b/src/main/java/org/jabref/logic/integrity/JournalInAbbreviationListChecker.java @@ -5,13 +5,12 @@ import java.util.Objects; import java.util.Optional; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; -public class JournalInAbbreviationListChecker implements Checker { +public class JournalInAbbreviationListChecker implements EntryChecker { private final Field field; private final JournalAbbreviationRepository abbreviationRepository; @@ -24,7 +23,7 @@ public JournalInAbbreviationListChecker(Field field, JournalAbbreviationReposito @Override public List check(BibEntry entry) { Optional value = entry.getField(field); - if (!value.isPresent()) { + if (value.isEmpty()) { return Collections.emptyList(); } diff --git a/src/main/java/org/jabref/logic/integrity/NoBibtexFieldChecker.java b/src/main/java/org/jabref/logic/integrity/NoBibtexFieldChecker.java index 8c3cc2b0198..c822a231a0a 100644 --- a/src/main/java/org/jabref/logic/integrity/NoBibtexFieldChecker.java +++ b/src/main/java/org/jabref/logic/integrity/NoBibtexFieldChecker.java @@ -4,7 +4,6 @@ import java.util.Set; import java.util.stream.Collectors; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.BibField; @@ -16,7 +15,7 @@ /** * This checker checks whether the entry does not contain any field appearing only in biblatex (and not in BibTeX) */ -public class NoBibtexFieldChecker implements Checker { +public class NoBibtexFieldChecker implements EntryChecker { private Set getAllBiblatexOnlyFields() { Set allBibtexFields = BibtexEntryTypeDefinitions.ALL.stream().flatMap(type -> type.getAllFields().stream()).collect(Collectors.toSet()); diff --git a/src/main/java/org/jabref/logic/integrity/TypeChecker.java b/src/main/java/org/jabref/logic/integrity/TypeChecker.java index 51e899f366c..f26db2a0e06 100644 --- a/src/main/java/org/jabref/logic/integrity/TypeChecker.java +++ b/src/main/java/org/jabref/logic/integrity/TypeChecker.java @@ -4,18 +4,17 @@ import java.util.List; import java.util.Optional; -import org.jabref.logic.integrity.IntegrityCheck.Checker; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; -public class TypeChecker implements Checker { +public class TypeChecker implements EntryChecker { @Override public List check(BibEntry entry) { Optional value = entry.getField(StandardField.PAGES); - if (!value.isPresent()) { + if (value.isEmpty()) { return Collections.emptyList(); } diff --git a/src/main/java/org/jabref/model/entry/identifier/DOI.java b/src/main/java/org/jabref/model/entry/identifier/DOI.java index c11a614cc18..742d4460430 100644 --- a/src/main/java/org/jabref/model/entry/identifier/DOI.java +++ b/src/main/java/org/jabref/model/entry/identifier/DOI.java @@ -2,6 +2,7 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.regex.Matcher; @@ -14,7 +15,8 @@ import org.slf4j.LoggerFactory; /** - * Class for working with Digital object identifiers (DOIs) and Short DOIs + * Class for working with Digital object identifiers + * (DOIs) and Short DOIs */ public class DOI implements Identifier { private static final Logger LOGGER = LoggerFactory.getLogger(DOI.class); @@ -117,7 +119,7 @@ public DOI(String doi) { /** * Creates an Optional<DOI> from various schemes including URL, URN, and plain DOIs. - * + *

* Useful for suppressing the IllegalArgumentException of the Constructor and checking for * Optional.isPresent() instead. * @@ -227,4 +229,24 @@ public Field getDefaultField() { public String getNormalized() { return doi; } + + /** + * DOIs are case-insensitive. Thus, 10.1109/cloud.2017.89 equals 10.1109/CLOUD.2017.89 + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DOI other = (DOI) o; + return doi.equalsIgnoreCase(other.doi); + } + + @Override + public int hashCode() { + return Objects.hash(doi.toLowerCase(Locale.ENGLISH)); + } } diff --git a/src/main/java/org/jabref/model/entry/identifier/Identifier.java b/src/main/java/org/jabref/model/entry/identifier/Identifier.java index 5207c29523a..3a921bfdf6c 100644 --- a/src/main/java/org/jabref/model/entry/identifier/Identifier.java +++ b/src/main/java/org/jabref/model/entry/identifier/Identifier.java @@ -7,9 +7,12 @@ public interface Identifier { - Field getDefaultField(); - + /** + * Returns the identifier. + */ String getNormalized(); + Field getDefaultField(); + Optional getExternalURI(); } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index cbf6b22bb31..d9e9532dd8a 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1311,6 +1311,7 @@ Copy\ title=Copy title Copy\ \\cite{BibTeX\ key}=Copy \\cite{BibTeX key} Copy\ BibTeX\ key\ and\ title=Copy BibTeX key and title Invalid\ DOI\:\ '%0'.=Invalid DOI: '%0'. +Same\ DOI\ used\ in\ multiple\ entries=Same DOI used in multiple entries should\ start\ with\ a\ name=should start with a name should\ end\ with\ a\ name=should end with a name unexpected\ closing\ curly\ bracket=unexpected closing curly bracket diff --git a/src/test/java/org/jabref/logic/integrity/DOIValidityCheckerTest.java b/src/test/java/org/jabref/logic/integrity/DoiValidityCheckerTest.java similarity index 89% rename from src/test/java/org/jabref/logic/integrity/DOIValidityCheckerTest.java rename to src/test/java/org/jabref/logic/integrity/DoiValidityCheckerTest.java index fd0893186c6..f2d74e4d5d8 100644 --- a/src/test/java/org/jabref/logic/integrity/DOIValidityCheckerTest.java +++ b/src/test/java/org/jabref/logic/integrity/DoiValidityCheckerTest.java @@ -7,9 +7,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -public class DOIValidityCheckerTest { +class DoiValidityCheckerTest { - private final DOIValidityChecker checker = new DOIValidityChecker(); + private final DoiValidityChecker checker = new DoiValidityChecker(); @Test void doiAcceptsValidInput() { diff --git a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java index 81a448f476a..1c45d40a5f5 100644 --- a/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java +++ b/src/test/java/org/jabref/logic/integrity/IntegrityCheckTest.java @@ -139,7 +139,7 @@ void testEntryIsUnchangedAfterChecks() { mock(FilePreferences.class), createBibtexKeyPatternPreferences(), JournalAbbreviationLoader.loadBuiltInRepository(), false) - .checkDatabase(); + .check(); assertEquals(clonedEntry, entry); } @@ -172,7 +172,7 @@ private void assertWrong(BibDatabaseContext context) { mock(FilePreferences.class), createBibtexKeyPatternPreferences(), JournalAbbreviationLoader.loadBuiltInRepository(), false) - .checkDatabase(); + .check(); assertNotEquals(Collections.emptyList(), messages); } @@ -181,7 +181,7 @@ private void assertCorrect(BibDatabaseContext context) { mock(FilePreferences.class), createBibtexKeyPatternPreferences(), JournalAbbreviationLoader.loadBuiltInRepository(), false - ).checkDatabase(); + ).check(); assertEquals(Collections.emptyList(), messages); } @@ -190,7 +190,7 @@ private void assertCorrect(BibDatabaseContext context, boolean allowIntegerEditi mock(FilePreferences.class), createBibtexKeyPatternPreferences(), JournalAbbreviationLoader.loadBuiltInRepository(), - allowIntegerEdition).checkDatabase(); + allowIntegerEdition).check(); assertEquals(Collections.emptyList(), messages); } diff --git a/src/test/java/org/jabref/model/entry/identifier/DOITest.java b/src/test/java/org/jabref/model/entry/identifier/DOITest.java index 44af0ec70a2..03cb93085f1 100644 --- a/src/test/java/org/jabref/model/entry/identifier/DOITest.java +++ b/src/test/java/org/jabref/model/entry/identifier/DOITest.java @@ -5,7 +5,9 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DOITest { @@ -228,11 +230,16 @@ public void parseShortDOIWithWhiteSpace() { @Test public void isShortDoiShouldReturnTrueWhenItIsShortDoi() { - assertEquals(true, new DOI("10/abcde").isShortDoi()); + assertTrue(new DOI("10/abcde").isShortDoi()); } @Test public void isShortDoiShouldReturnFalseWhenItIsDoi() { - assertEquals(false, new DOI("10.1006/jmbi.1998.2354").isShortDoi()); + assertFalse(new DOI("10.1006/jmbi.1998.2354").isShortDoi()); + } + + @Test + public void equalsWorksFor2017Doi() { + assertTrue(new DOI("10.1109/cloud.2017.89").equals(new DOI("10.1109/CLOUD.2017.89"))); } }