package org.jabref.model.database; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.jabref.model.database.event.EntryAddedEvent; import org.jabref.model.database.event.EntryRemovedEvent; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.event.FieldChangedEvent; import com.google.common.eventbus.Subscribe; /** * Determines which bibtex cite keys are duplicates in a single {@link BibDatabase}. */ public class DuplicationChecker { /** use a map instead of a set since I need to know how many of each key is in there */ private final Map<String, Integer> allKeys = new HashMap<>(); /** * Checks if there is more than one occurrence of this key */ public boolean isDuplicateCiteKeyExisting(String citeKey) { return getNumberOfKeyOccurrences(citeKey) > 1; } /** * Checks if there is more than one occurrence of the cite key */ public boolean isDuplicateCiteKeyExisting(BibEntry entry) { return isDuplicateCiteKeyExisting(entry.getCiteKeyOptional().orElse(null)); } /** * Returns the number of occurrences of the given key in this database. */ public int getNumberOfKeyOccurrences(String citeKey) { return allKeys.getOrDefault(citeKey, 0); } /** * Helper function for counting the number of the key usages. * Adds the given key to the internal keyset together with the count of it. * The counter is increased if the key already exists, otherwise set to 1. * <br> * Special case: If a null or empty key is passed, it is not counted and thus not added. * * Reasoning: * Consider this: I add a key xxx, then I add another key xxx. I get a warning. I delete the key xxx. * Consider JabRef simply removing this key from a set of allKeys. * Then I add another key xxx. I don't get a warning! * Thus, I need a way to count the number of keys of each type. * Solution: hashmap=>int (increment each time at add and decrement each time at remove) */ private void addKeyToSet(String key) { if (key == null || key.isEmpty()) { return; } allKeys.put(key, getNumberOfKeyOccurrences(key) + 1); } /** * Helper function for counting the number of the key usages. * Removes the given key from the internal keyset together with the count of it, if the key is set to 1. * If it is not set to 1, the counter will be decreased. * <br> * Special case: If a null or empty key is passed, it is not counted and thus not removed. */ private void removeKeyFromSet(String key) { if (key == null || key.isEmpty()) { return; } int numberOfKeyOccurrences = getNumberOfKeyOccurrences(key); if (numberOfKeyOccurrences > 1) { allKeys.put(key, numberOfKeyOccurrences - 1); } else { allKeys.remove(key); } } @Subscribe public void listen(FieldChangedEvent fieldChangedEvent) { if (fieldChangedEvent.getFieldName().equals(BibEntry.KEY_FIELD)) { removeKeyFromSet(fieldChangedEvent.getOldValue()); addKeyToSet(fieldChangedEvent.getNewValue()); } } @Subscribe public void listen(EntryRemovedEvent entryRemovedEvent) { Optional<String> citeKey = entryRemovedEvent.getBibEntry().getCiteKeyOptional(); if (citeKey.isPresent()) { removeKeyFromSet(citeKey.get()); } } @Subscribe public void listen(EntryAddedEvent entryAddedEvent) { Optional<String> citekey = entryAddedEvent.getBibEntry().getCiteKeyOptional(); if (citekey.isPresent()) { addKeyToSet(citekey.get()); } } }