package org.jabref.logic.autocompleter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import org.jabref.model.entry.Author;
import org.jabref.model.entry.AuthorList;
import org.jabref.model.entry.BibEntry;
/**
* Delivers possible completions for a given string.
* Interprets the given values as names and stores them in different
* permutations so we can complete by beginning with last name or first name.
*
* @author kahlert, cordes
*/
class NameFieldAutoCompleter extends AbstractAutoCompleter {
private final List<String> fieldNames;
/**
* true if only last names should be completed and there is NO separation by " and ", but by " "
*/
private final boolean lastNameOnlyAndSeparationBySpace;
private final boolean autoCompFF;
private final boolean autoCompLF;
private final AutoCompleteFirstNameMode autoCompFirstnameMode;
private String prefix = "";
/**
* @see AutoCompleterFactory
*/
NameFieldAutoCompleter(String fieldName, AutoCompletePreferences preferences) {
this(Collections.singletonList(Objects.requireNonNull(fieldName)), false, preferences);
}
public NameFieldAutoCompleter(List<String> fieldNames, boolean lastNameOnlyAndSeparationBySpace,
AutoCompletePreferences preferences) {
super(preferences);
this.fieldNames = Objects.requireNonNull(fieldNames);
this.lastNameOnlyAndSeparationBySpace = lastNameOnlyAndSeparationBySpace;
if (preferences.getOnlyCompleteFirstLast()) {
autoCompFF = true;
autoCompLF = false;
} else if (preferences.getOnlyCompleteLastFirst()) {
autoCompFF = false;
autoCompLF = true;
} else {
autoCompFF = true;
autoCompLF = true;
}
autoCompFirstnameMode = preferences.getFirstnameMode() == null ? AutoCompleteFirstNameMode.BOTH : preferences
.getFirstnameMode();
}
@Override
public boolean isSingleUnitField() {
// quick hack
// when used at entry fields (!this.lastNameOnlyAndSeparationBySpace), this is a single unit field
// when used at the search form (this.lastNameOnlyAndSeparationBySpace), this is NOT a single unit field
// reason: search keywords are separated by space.
// This is OK for last names without prefix. "Lastname" works perfectly.
// querying for "van der Lastname" can be interpreted as
// a) "van" "der" "Lastname"
// b) "van der Lastname" (autocompletion lastname)
return !this.lastNameOnlyAndSeparationBySpace;
}
@Override
public void addBibtexEntry(BibEntry entry) {
if (entry == null) {
return;
}
for (String fieldName : fieldNames) {
entry.getField(fieldName).ifPresent(fieldValue -> {
AuthorList authorList = AuthorList.parse(fieldValue);
for (Author author : authorList.getAuthors()) {
handleAuthor(author);
}
});
}
}
/**
* SIDE EFFECT: sets class variable prefix
* Delimiter: " and " or " "
*
* @return String without prefix
*/
private String determinePrefixAndReturnRemainder(String str, String delimiter) {
String result = str;
int index = result.toLowerCase(Locale.ROOT).lastIndexOf(delimiter);
if (index >= 0) {
prefix = result.substring(0, index + delimiter.length());
result = result.substring(index + delimiter.length());
} else {
prefix = "";
}
return result;
}
private void handleAuthor(Author author) {
if (lastNameOnlyAndSeparationBySpace) {
addItemToIndex(author.getLastOnly());
} else {
if (autoCompLF) {
switch (autoCompFirstnameMode) {
case ONLY_ABBREVIATED:
addItemToIndex(author.getLastFirst(true));
break;
case ONLY_FULL:
addItemToIndex(author.getLastFirst(false));
break;
case BOTH:
addItemToIndex(author.getLastFirst(true));
addItemToIndex(author.getLastFirst(false));
break;
default:
break;
}
}
if (autoCompFF) {
switch (autoCompFirstnameMode) {
case ONLY_ABBREVIATED:
addItemToIndex(author.getFirstLast(true));
break;
case ONLY_FULL:
addItemToIndex(author.getFirstLast(false));
break;
case BOTH:
addItemToIndex(author.getFirstLast(true));
addItemToIndex(author.getFirstLast(false));
break;
default:
break;
}
}
}
}
@Override
public List<String> complete(String toComplete) {
if (toComplete == null) {
return new ArrayList<>();
}
String result;
// Normally, one would implement that using
// class inheritance. But this seemed overengineered
if (this.lastNameOnlyAndSeparationBySpace) {
result = determinePrefixAndReturnRemainder(toComplete, " ");
} else {
result = determinePrefixAndReturnRemainder(toComplete, " and ");
}
return super.complete(result);
}
@Override
public String getPrefix() {
return prefix;
}
@Override
protected int getLengthOfShortestWordToAdd() {
return 1;
}
}