diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ade463b0d6c..2b267604f65 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -12,7 +12,7 @@ - + diff --git a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java index cbd68c085a3..1d368e5496c 100644 --- a/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java +++ b/src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java @@ -14,13 +14,11 @@ import javafx.beans.value.ObservableValue; import org.jabref.gui.specialfields.SpecialFieldValueViewModel; -import org.jabref.model.database.BibDatabase; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.FileFieldParser; import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.field.Field; -import org.jabref.model.entry.field.FieldProperty; import org.jabref.model.entry.field.OrFields; import org.jabref.model.entry.field.SpecialField; import org.jabref.model.entry.field.StandardField; @@ -33,22 +31,20 @@ public class BibEntryTableViewModel { private final BibEntry entry; - private final BibDatabase database; - private final ObservableValue nameFormatter; + private final ObservableValue fieldValueFormatter; private final Map> fieldValues = new HashMap<>(); private final Map> specialFieldValues = new HashMap<>(); private final EasyBinding> linkedFiles; private final EasyBinding> linkedIdentifiers; private final ObservableValue> matchedGroups; - public BibEntryTableViewModel(BibEntry entry, BibDatabaseContext database, ObservableValue nameFormatter) { + public BibEntryTableViewModel(BibEntry entry, BibDatabaseContext bibDatabaseContext, ObservableValue fieldValueFormatter) { this.entry = entry; - this.database = database.getDatabase(); - this.nameFormatter = nameFormatter; + this.fieldValueFormatter = fieldValueFormatter; this.linkedFiles = getField(StandardField.FILE).map(FileFieldParser::parse).orElse(Collections.emptyList()); this.linkedIdentifiers = createLinkedIdentifiersBinding(entry); - this.matchedGroups = createMatchedGroupsBinding(database, entry); + this.matchedGroups = createMatchedGroupsBinding(bibDatabaseContext, entry); } private static EasyBinding> createLinkedIdentifiersBinding(BibEntry entry) { @@ -120,26 +116,11 @@ public ObservableValue getFields(OrFields fields) { } ArrayList observables = new ArrayList<>(List.of(entry.getObservables())); - observables.add(nameFormatter); - - value = Bindings.createStringBinding(() -> { - for (Field field : fields) { - if (field.getProperties().contains(FieldProperty.PERSON_NAMES)) { - Optional name = entry.getResolvedFieldOrAlias(field, database); - - if (name.isPresent()) { - return nameFormatter.getValue().formatNameLatexFree(name.get()); - } - } else { - Optional content = entry.getResolvedFieldOrAliasLatexFree(field, database); - - if (content.isPresent()) { - return content.get(); - } - } - } - return ""; - }, observables.toArray(Observable[]::new)); + observables.add(fieldValueFormatter); + + value = Bindings.createStringBinding(() -> + fieldValueFormatter.getValue().formatFieldsValues(fields, entry), + observables.toArray(Observable[]::new)); fieldValues.put(fields, value); return value; } diff --git a/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java b/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java index e9f6ecb5b82..2a7e6f176c8 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableDataModel.java @@ -29,15 +29,19 @@ public class MainTableDataModel { private final FilteredList entriesFiltered; private final SortedList entriesSorted; private final GroupViewMode groupViewMode; - private final ObjectProperty nameFormatter; + private final ObjectProperty fieldValueFormatter; private final PreferencesService preferencesService; + private final BibDatabaseContext bibDatabaseContext; public MainTableDataModel(BibDatabaseContext context, PreferencesService preferencesService, StateManager stateManager) { - this.nameFormatter = new SimpleObjectProperty<>(new MainTableNameFormatter(preferencesService)); this.preferencesService = preferencesService; + this.bibDatabaseContext = context; + this.fieldValueFormatter = new SimpleObjectProperty<>( + new MainTableFieldValueFormatter(preferencesService, bibDatabaseContext)); ObservableList allEntries = BindingsHelper.forUI(context.getDatabase().getEntries()); - ObservableList entriesViewModel = EasyBind.mapBacked(allEntries, entry -> new BibEntryTableViewModel(entry, context, nameFormatter)); + ObservableList entriesViewModel = EasyBind.mapBacked(allEntries, entry -> + new BibEntryTableViewModel(entry, bibDatabaseContext, fieldValueFormatter)); entriesFiltered = new FilteredList<>(entriesViewModel); entriesFiltered.predicateProperty().bind( @@ -86,6 +90,6 @@ public SortedList getEntriesFilteredAndSorted() { } public void refresh() { - this.nameFormatter.setValue(new MainTableNameFormatter(preferencesService)); + this.fieldValueFormatter.setValue(new MainTableFieldValueFormatter(preferencesService, bibDatabaseContext)); } } diff --git a/src/main/java/org/jabref/gui/maintable/MainTableFieldValueFormatter.java b/src/main/java/org/jabref/gui/maintable/MainTableFieldValueFormatter.java new file mode 100644 index 00000000000..6c83a0a2a04 --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/MainTableFieldValueFormatter.java @@ -0,0 +1,87 @@ +package org.jabref.gui.maintable; + +import java.util.Optional; + +import org.jabref.model.database.BibDatabase; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.AuthorList; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.FieldProperty; +import org.jabref.model.entry.field.OrFields; +import org.jabref.preferences.PreferencesService; + +import static org.jabref.gui.maintable.MainTableNameFormatPreferences.AbbreviationStyle; +import static org.jabref.gui.maintable.MainTableNameFormatPreferences.DisplayStyle; + +public class MainTableFieldValueFormatter { + private final DisplayStyle displayStyle; + private final AbbreviationStyle abbreviationStyle; + private final BibDatabase bibDatabase; + + MainTableFieldValueFormatter(PreferencesService preferences, BibDatabaseContext bibDatabaseContext) { + MainTableNameFormatPreferences nameFormatPreferences = preferences.getMainTableNameFormatPreferences(); + this.displayStyle = nameFormatPreferences.getDisplayStyle(); + this.abbreviationStyle = nameFormatPreferences.getAbbreviationStyle(); + this.bibDatabase = bibDatabaseContext.getDatabase(); + } + + /** + * Format fields for {@link BibEntryTableViewModel}, according to user preferences and with latex translated to + * unicode if possible. + * + * @param fields the fields argument of {@link BibEntryTableViewModel#getFields(OrFields)}. + * @param entry the BibEntry of {@link BibEntryTableViewModel}. + * @return The formatted name field. + */ + public String formatFieldsValues(final OrFields fields, final BibEntry entry) { + for (Field field : fields) { + if (field.getProperties().contains(FieldProperty.PERSON_NAMES) && displayStyle != DisplayStyle.AS_IS) { + Optional name = entry.getResolvedFieldOrAlias(field, bibDatabase); + + if (name.isPresent()) { + return formatFieldWithAuthorValue(name.get()); + } + } else { + Optional content = entry.getResolvedFieldOrAliasLatexFree(field, bibDatabase); + + if (content.isPresent()) { + return content.get(); + } + } + } + return ""; + } + + /** + * Format a name field for the table, according to user preferences and with latex expressions translated if + * possible. + * + * @param nameToFormat The contents of the name field. + * @return The formatted name field. + */ + private String formatFieldWithAuthorValue(final String nameToFormat) { + if (nameToFormat == null) { + return null; + } + + AuthorList authors = AuthorList.parse(nameToFormat); + + if (((displayStyle == DisplayStyle.FIRSTNAME_LASTNAME) + || (displayStyle == DisplayStyle.LASTNAME_FIRSTNAME)) + && abbreviationStyle == AbbreviationStyle.LASTNAME_ONLY) { + return authors.getAsLastNamesLatexFree(false); + } + + return switch (displayStyle) { + default -> nameToFormat; + case FIRSTNAME_LASTNAME -> authors.getAsFirstLastNamesLatexFree( + abbreviationStyle == AbbreviationStyle.FULL, + false); + case LASTNAME_FIRSTNAME -> authors.getAsLastFirstNamesLatexFree( + abbreviationStyle == AbbreviationStyle.FULL, + false); + case NATBIB -> authors.getAsNatbibLatexFree(); + }; + } +} diff --git a/src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java b/src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java deleted file mode 100644 index 06766cdecbe..00000000000 --- a/src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.jabref.gui.maintable; - -import org.jabref.model.entry.AuthorList; -import org.jabref.preferences.PreferencesService; - -public class MainTableNameFormatter { - - private final PreferencesService preferencesService; - - MainTableNameFormatter(PreferencesService preferences) { - this.preferencesService = preferences; - } - - /** - * Format a name field for the table, according to user preferences and with latex expressions translated if - * possible. - * - * @param nameToFormat The contents of the name field. - * @return The formatted name field. - */ - public String formatNameLatexFree(final String nameToFormat) { - if (nameToFormat == null) { - return null; - } - - MainTableNameFormatPreferences nameFormatPreferences = preferencesService.getMainTableNameFormatPreferences(); - MainTableNameFormatPreferences.DisplayStyle displayStyle = nameFormatPreferences.getDisplayStyle(); - MainTableNameFormatPreferences.AbbreviationStyle abbreviationStyle = nameFormatPreferences.getAbbreviationStyle(); - - AuthorList authors = AuthorList.parse(nameToFormat); - - if (((displayStyle == MainTableNameFormatPreferences.DisplayStyle.FIRSTNAME_LASTNAME) - || (displayStyle == MainTableNameFormatPreferences.DisplayStyle.LASTNAME_FIRSTNAME)) - && abbreviationStyle == MainTableNameFormatPreferences.AbbreviationStyle.LASTNAME_ONLY) { - return authors.getAsLastNamesLatexFree(false); - } - - switch (nameFormatPreferences.getDisplayStyle()) { - case AS_IS: - return nameToFormat; - case NATBIB: - return authors.getAsNatbibLatexFree(); - case FIRSTNAME_LASTNAME: - return authors.getAsFirstLastNamesLatexFree( - abbreviationStyle == MainTableNameFormatPreferences.AbbreviationStyle.FULL, - false); - default: - case LASTNAME_FIRSTNAME: - return authors.getAsLastFirstNamesLatexFree( - abbreviationStyle == MainTableNameFormatPreferences.AbbreviationStyle.FULL, - false); - } - } -} diff --git a/src/main/java/org/jabref/model/entry/Author.java b/src/main/java/org/jabref/model/entry/Author.java index 32be4e9ce96..b325a3fa92f 100644 --- a/src/main/java/org/jabref/model/entry/Author.java +++ b/src/main/java/org/jabref/model/entry/Author.java @@ -146,8 +146,8 @@ public int hashCode() { /** * Compare this object with the given one. - *

- * Will return true iff the other object is an Author and all fields are identical on a string comparison. + * + * @return `true` iff the other object is an Author and all fields are `Objects.equals`. */ @Override public boolean equals(Object other) { diff --git a/src/main/java/org/jabref/model/entry/AuthorList.java b/src/main/java/org/jabref/model/entry/AuthorList.java index b617b792632..4f2467da8f9 100644 --- a/src/main/java/org/jabref/model/entry/AuthorList.java +++ b/src/main/java/org/jabref/model/entry/AuthorList.java @@ -1,12 +1,8 @@ package org.jabref.model.entry; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Objects; import java.util.WeakHashMap; import java.util.stream.Collectors; @@ -123,8 +119,6 @@ public class AuthorList { private static final WeakHashMap AUTHOR_CACHE = new WeakHashMap<>(); - // Avoid partition where these values are contained - private final static Collection AVOID_TERMS_IN_LOWER_CASE = Arrays.asList("jr", "sr", "jnr", "snr", "von", "zu", "van", "der"); private final List authors; private final String[] authorsFirstFirst = new String[4]; private final String[] authorsFirstFirstLatexFree = new String[4]; @@ -157,7 +151,7 @@ protected AuthorList(Author author) { } public AuthorList() { - this(new ArrayList()); + this(new ArrayList<>()); } /** @@ -168,53 +162,9 @@ public AuthorList() { * @param authors The string of authors or editors in bibtex format to parse. * @return An AuthorList object representing the given authors. */ - public static AuthorList parse(String authors) { + public static AuthorList parse(final String authors) { Objects.requireNonNull(authors); - // Handle case names in order lastname, firstname and separated by "," - // E.g., Ali Babar, M., Dingsøyr, T., Lago, P., van der Vliet, H. - final boolean authorsContainAND = authors.toUpperCase(Locale.ENGLISH).contains(" AND "); - final boolean authorsContainOpeningBrace = authors.contains("{"); - final boolean authorsContainSemicolon = authors.contains(";"); - final boolean authorsContainTwoOrMoreCommas = (authors.length() - authors.replace(",", "").length()) >= 2; - if (!authorsContainAND && !authorsContainOpeningBrace && !authorsContainSemicolon && authorsContainTwoOrMoreCommas) { - List arrayNameList = Arrays.asList(authors.split(",")); - - // Delete spaces for correct case identification - arrayNameList.replaceAll(String::trim); - - // Looking for space between pre- and lastname - boolean spaceInAllParts = arrayNameList.stream().filter(name -> name.contains(" ")).collect(Collectors - .toList()).size() == arrayNameList.size(); - - // We hit the comma name separator case - // Usually the getAsLastFirstNamesWithAnd method would separate them if pre- and lastname are separated with "and" - // If not, we check if spaces separate pre- and lastname - if (spaceInAllParts) { - authors = authors.replaceAll(",", " and"); - } else { - // Looking for name affixes to avoid - // arrayNameList needs to reduce by the count off avoiding terms - // valuePartsCount holds the count of name parts without the avoided terms - - int valuePartsCount = arrayNameList.size(); - // Holds the index of each term which needs to be avoided - Collection avoidIndex = new HashSet<>(); - - for (int i = 0; i < arrayNameList.size(); i++) { - if (AVOID_TERMS_IN_LOWER_CASE.contains(arrayNameList.get(i).toLowerCase(Locale.ROOT))) { - avoidIndex.add(i); - valuePartsCount--; - } - } - - if ((valuePartsCount % 2) == 0) { - // We hit the described special case with name affix like Jr - authors = buildWithAffix(avoidIndex, arrayNameList).toString(); - } - } - } - AuthorList authorList = AUTHOR_CACHE.get(authors); if (authorList == null) { AuthorListParser parser = new AuthorListParser(); @@ -296,37 +246,6 @@ public static String fixAuthorNatbib(String authors) { return AuthorList.parse(authors).getAsNatbib(); } - /** - * Builds a new array of strings with stringbuilder. - * Regarding to the name affixes. - * - * @return New string with correct seperation - */ - private static StringBuilder buildWithAffix(Collection indexArray, List nameList) { - StringBuilder stringBuilder = new StringBuilder(); - // avoidedTimes needs to be increased by the count of avoided terms for correct odd/even calculation - int avoidedTimes = 0; - for (int i = 0; i < nameList.size(); i++) { - if (indexArray.contains(i)) { - // We hit a name affix - stringBuilder.append(nameList.get(i)); - stringBuilder.append(','); - avoidedTimes++; - } else { - stringBuilder.append(nameList.get(i)); - if (((i + avoidedTimes) % 2) == 0) { - // Hit separation between last name and firstname --> comma has to be kept - stringBuilder.append(','); - } else { - // Hit separation between full names (e.g., Ali Babar, M. and Dingsøyr, T.) --> semicolon has to be used - // Will be treated correctly by AuthorList.parse(authors); - stringBuilder.append(';'); - } - } - } - return stringBuilder; - } - /** * Returns the number of author names in this object. * @@ -647,10 +566,14 @@ public String getAsFirstLastNamesLatexFree(boolean abbreviate, boolean oxfordCom /** * Compare this object with the given one. *

- * Will return true iff the other object is an Author and all fields are identical on a string comparison. + * @return `true` iff the other object is an AuthorList, all contained authors are in the same order (and these + * authors' fields are `Objects.equals`) */ @Override public boolean equals(Object o) { + if (this == o) { + return true; + } if (!(o instanceof AuthorList)) { return false; } diff --git a/src/main/java/org/jabref/model/entry/AuthorListParser.java b/src/main/java/org/jabref/model/entry/AuthorListParser.java index d584868f17c..eca4ee598e9 100644 --- a/src/main/java/org/jabref/model/entry/AuthorListParser.java +++ b/src/main/java/org/jabref/model/entry/AuthorListParser.java @@ -1,6 +1,8 @@ package org.jabref.model.entry; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -10,6 +12,10 @@ public class AuthorListParser { + // Avoid partition where these values are contained + private final static Set AVOID_TERMS_IN_LOWER_CASE = Set.of( + "jr", "sr", "jnr", "snr", "von", "zu", "van", "der"); + private static final int TOKEN_GROUP_LENGTH = 4; // number of entries for a token // the following are offsets of an entry in a group of entries for one token @@ -21,31 +27,16 @@ public class AuthorListParser { // "-") comma) // Token types (returned by getToken procedure) - private static final int TOKEN_EOF = 0; - - private static final int TOKEN_AND = 1; - - private static final int TOKEN_COMMA = 2; - - private static final int TOKEN_WORD = 3; + private enum Token { + EOF, + AND, + COMMA, + WORD + } // Constant HashSet containing names of TeX special characters - private static final Set TEX_NAMES = new HashSet<>(); - - static { - TEX_NAMES.add("aa"); - TEX_NAMES.add("ae"); - TEX_NAMES.add("l"); - TEX_NAMES.add("o"); - TEX_NAMES.add("oe"); - TEX_NAMES.add("i"); - TEX_NAMES.add("AA"); - TEX_NAMES.add("AE"); - TEX_NAMES.add("L"); - TEX_NAMES.add("O"); - TEX_NAMES.add("OE"); - TEX_NAMES.add("j"); - } + private static final Set TEX_NAMES = Set.of( + "aa", "ae", "l", "o", "oe", "i", "AA", "AE", "L", "O", "OE", "j"); /** * the raw bibtex author/editor field @@ -61,7 +52,7 @@ public class AuthorListParser { private int tokenEnd; /** * end of token abbreviation (always: tokenStart < tokenAbbrEnd <= tokenEnd), only valid if getToken returns - * TOKEN_WORD + * Token.WORD */ private int tokenAbbrEnd; /** @@ -82,6 +73,50 @@ public class AuthorListParser { public AuthorList parse(String listOfNames) { Objects.requireNonNull(listOfNames); + // Handle case names in order lastname, firstname and separated by "," + // E.g., Ali Babar, M., Dingsøyr, T., Lago, P., van der Vliet, H. + final boolean authorsContainAND = listOfNames.toUpperCase(Locale.ENGLISH).contains(" AND "); + final boolean authorsContainOpeningBrace = listOfNames.contains("{"); + final boolean authorsContainSemicolon = listOfNames.contains(";"); + final boolean authorsContainTwoOrMoreCommas = (listOfNames.length() - listOfNames.replace(",", "").length()) >= 2; + if (!authorsContainAND && !authorsContainOpeningBrace && !authorsContainSemicolon && authorsContainTwoOrMoreCommas) { + List arrayNameList = Arrays.asList(listOfNames.split(",")); + + // Delete spaces for correct case identification + arrayNameList.replaceAll(String::trim); + + // Looking for space between pre- and lastname + boolean spaceInAllParts = arrayNameList.stream().filter(name -> name.contains(" ")) + .count() == arrayNameList.size(); + + // We hit the comma name separator case + // Usually the getAsLastFirstNamesWithAnd method would separate them if pre- and lastname are separated with "and" + // If not, we check if spaces separate pre- and lastname + if (spaceInAllParts) { + listOfNames = listOfNames.replaceAll(",", " and"); + } else { + // Looking for name affixes to avoid + // arrayNameList needs to reduce by the count off avoiding terms + // valuePartsCount holds the count of name parts without the avoided terms + + int valuePartsCount = arrayNameList.size(); + // Holds the index of each term which needs to be avoided + Collection avoidIndex = new HashSet<>(); + + for (int i = 0; i < arrayNameList.size(); i++) { + if (AVOID_TERMS_IN_LOWER_CASE.contains(arrayNameList.get(i).toLowerCase(Locale.ROOT))) { + avoidIndex.add(i); + valuePartsCount--; + } + } + + if ((valuePartsCount % 2) == 0) { + // We hit the described special case with name affix like Jr + listOfNames = buildWithAffix(avoidIndex, arrayNameList).toString(); + } + } + } + // initialization of parser original = listOfNames; tokenStart = 0; @@ -95,6 +130,37 @@ public AuthorList parse(String listOfNames) { return new AuthorList(authors); } + /** + * Builds a new array of strings with stringbuilder. + * Regarding to the name affixes. + * + * @return New string with correct seperation + */ + private static StringBuilder buildWithAffix(Collection indexArray, List nameList) { + StringBuilder stringBuilder = new StringBuilder(); + // avoidedTimes needs to be increased by the count of avoided terms for correct odd/even calculation + int avoidedTimes = 0; + for (int i = 0; i < nameList.size(); i++) { + if (indexArray.contains(i)) { + // We hit a name affix + stringBuilder.append(nameList.get(i)); + stringBuilder.append(','); + avoidedTimes++; + } else { + stringBuilder.append(nameList.get(i)); + if (((i + avoidedTimes) % 2) == 0) { + // Hit separation between last name and firstname --> comma has to be kept + stringBuilder.append(','); + } else { + // Hit separation between full names (e.g., Ali Babar, M. and Dingsøyr, T.) --> semicolon has to be used + // Will be treated correctly by AuthorList.parse(authors); + stringBuilder.append(';'); + } + } + } + return stringBuilder; + } + /** * Parses one author name and returns preformatted information. * @@ -111,20 +177,20 @@ private Optional getAuthor() { // First step: collect tokens in 'tokens' Vector and calculate indices boolean continueLoop = true; while (continueLoop) { - int token = getToken(); + Token token = getToken(); switch (token) { - case TOKEN_EOF: - case TOKEN_AND: + case EOF: + case AND: continueLoop = false; break; - case TOKEN_COMMA: + case COMMA: if (commaFirst < 0) { commaFirst = tokens.size(); } else if (commaSecond < 0) { commaSecond = tokens.size(); } break; - case TOKEN_WORD: + case WORD: tokens.add(original.substring(tokenStart, tokenEnd)); tokens.add(original.substring(tokenStart, tokenAbbrEnd)); tokens.add(tokenTerm); @@ -305,31 +371,31 @@ private String concatTokens(List tokens, int start, int end, int offset, /** * Parses the next token. *

- * The string being parsed is stored in global variable orig, + * The string being parsed is stored in global variable original, * and position which parsing has to start from is stored in global variable * token_end; thus, token_end has to be set * to 0 before the first invocation. Procedure updates token_end; * thus, subsequent invocations do not require any additional variable * settings. *

- * The type of the token is returned; if it is TOKEN_WORD, + * The type of the token is returned; if it is Token.WORD, * additional information is given in global variables token_start, * token_end, token_abbr, token_term, - * and token_case; namely: orig.substring(token_start,token_end) - * is the text of the token, orig.substring(token_start,token_abbr) + * and token_case; namely: original.substring(token_start,token_end) + * is the text of the token, original.substring(token_start,token_abbr) * is the token abbreviation, token_term contains token * terminator (space or dash), and token_case is true, * if token is upper-case and false if token is lower-case. * - * @return TOKEN_EOF -- no more tokens, TOKEN_COMMA -- - * token is comma, TOKEN_AND -- token is the word - * "and" (or "And", or "aND", etc.) or a semicolon, TOKEN_WORD -- + * @return Token.EOF -- no more tokens, Token.COMMA -- + * token is comma, Token.AND -- token is the word + * "and" (or "And", or "aND", etc.) or a semicolon, Token.WORD -- * token is a word; additional information is given in global * variables token_start, token_end, * token_abbr, token_term, and * token_case. */ - private int getToken() { + private Token getToken() { tokenStart = tokenEnd; while (tokenStart < original.length()) { char c = original.charAt(tokenStart); @@ -340,16 +406,16 @@ private int getToken() { } tokenEnd = tokenStart; if (tokenStart >= original.length()) { - return TOKEN_EOF; + return Token.EOF; } if (original.charAt(tokenStart) == ',') { tokenEnd++; - return TOKEN_COMMA; + return Token.COMMA; } // Semicolon is considered to separate names like "and" if (original.charAt(tokenStart) == ';') { tokenEnd++; - return TOKEN_AND; + return Token.AND; } tokenAbbrEnd = -1; tokenTerm = ' '; @@ -405,9 +471,9 @@ private int getToken() { tokenTerm = '-'; } if ("and".equalsIgnoreCase(original.substring(tokenStart, tokenEnd))) { - return TOKEN_AND; + return Token.AND; } else { - return TOKEN_WORD; + return Token.WORD; } } } diff --git a/src/test/java/org/jabref/model/entry/AuthorListParameterTest.java b/src/test/java/org/jabref/model/entry/AuthorListParserTest.java similarity index 72% rename from src/test/java/org/jabref/model/entry/AuthorListParameterTest.java rename to src/test/java/org/jabref/model/entry/AuthorListParserTest.java index 4947265355b..c2112663f65 100644 --- a/src/test/java/org/jabref/model/entry/AuthorListParameterTest.java +++ b/src/test/java/org/jabref/model/entry/AuthorListParserTest.java @@ -2,21 +2,22 @@ import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; -class AuthorListParameterTest { +class AuthorListParserTest { private static Stream data() { return Stream.of( Arguments.of("王, 军", new Author("军", "军.", null, "王", null)), Arguments.of("Doe, John", new Author("John", "J.", null, "Doe", null)), Arguments.of("von Berlichingen zu Hornberg, Johann Gottfried", new Author("Johann Gottfried", "J. G.", "von", "Berlichingen zu Hornberg", null)), - // Arguments.of("Robert and Sons, Inc.", new Author(null, null, null, "Robert and Sons, Inc.", null))), - // Arguments.of("al-Ṣāliḥ, Abdallāh", new Author("Abdallāh", "A.", null, "al-Ṣāliḥ", null))), + Arguments.of("{Robert and Sons, Inc.}", new Author(null, null, null, "Robert and Sons, Inc.", null)), + Arguments.of("al-Ṣāliḥ, Abdallāh", new Author("Abdallāh", "A.", null, "al-Ṣāliḥ", null)), Arguments.of("de la Vallée Poussin, Jean Charles Gabriel", new Author("Jean Charles Gabriel", "J. C. G.", "de la", "Vallée Poussin", null)), Arguments.of("de la Vallée Poussin, J. C. G.", new Author("J. C. G.", "J. C. G.", "de la", "Vallée Poussin", null)), Arguments.of("{K}ent-{B}oswell, E. S.", new Author("E. S.", "E. S.", null, "{K}ent-{B}oswell", null)), @@ -30,4 +31,11 @@ void parseCorrectly(String authorsString, Author authorsParsed) { AuthorListParser parser = new AuthorListParser(); assertEquals(new AuthorList(authorsParsed), parser.parse(authorsString)); } + + @Test + public void parseAuthorWithFirstNameAbbreviationContainingUmlaut() { + assertEquals(new AuthorList( + new Author("{\\OE}rjan", "{\\OE}.", null, "Umlauts", null)), + new AuthorListParser().parse("{\\OE}rjan Umlauts")); + } } diff --git a/src/test/java/org/jabref/model/entry/AuthorListTest.java b/src/test/java/org/jabref/model/entry/AuthorListTest.java index eda59858ac6..949368a3f33 100644 --- a/src/test/java/org/jabref/model/entry/AuthorListTest.java +++ b/src/test/java/org/jabref/model/entry/AuthorListTest.java @@ -1,6 +1,8 @@ package org.jabref.model.entry; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -8,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -18,47 +21,30 @@ public class AuthorListTest { Examples are similar to page 4 in [BibTeXing by Oren Patashnik](https://ctan.org/tex-archive/biblio/bibtex/contrib/doc/) */ - private static final String MUHAMMAD_ALKHWARIZMI = "Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}}"; - private static final String CORRADO_BOHM = "Corrado B{\\\"o}hm"; - private static final String KURT_GODEL = "Kurt G{\\\"{o}}del"; - private static final String BANU_MOSA = "{The Ban\\={u} M\\={u}s\\={a} brothers}"; + private static final Author MUHAMMAD_ALKHWARIZMI = + new Author("Mu{\\d{h}}ammad", "M.", null, "al-Khw{\\={a}}rizm{\\={i}}", null); + private static final Author CORRADO_BOHM = + new Author("Corrado", "C.", null, "B{\\\"o}hm", null); + private static final Author KURT_GODEL = + new Author("Kurt", "K.", null, "G{\\\"{o}}del", null); + private static final Author BANU_MOSA = + new Author(null, null, null, "{The Ban\\={u} M\\={u}s\\={a} brothers}", null); + private static final AuthorList EMPTY_AUTHOR = new AuthorList(Collections.emptyList()); + private static final AuthorList ONE_AUTHOR_WITH_LATEX = new AuthorList(MUHAMMAD_ALKHWARIZMI); + private static final AuthorList TWO_AUTHORS_WITH_LATEX = new AuthorList(List.of(MUHAMMAD_ALKHWARIZMI, CORRADO_BOHM)); + private static final AuthorList THREE_AUTHORS_WITH_LATEX = new AuthorList( + List.of(MUHAMMAD_ALKHWARIZMI, CORRADO_BOHM, KURT_GODEL)); + private static final AuthorList ONE_INSTITUTION_WITH_LATEX = new AuthorList(BANU_MOSA); + private static final AuthorList ONE_INSTITUTION_WITH_STARTING_PARANTHESIS = new AuthorList(new Author( + null, null, null, "{{\\L{}}ukasz Micha\\l{}}", null)); + private static final AuthorList TWO_INSTITUTIONS_WITH_LATEX = new AuthorList(List.of(BANU_MOSA, BANU_MOSA)); + private static final AuthorList MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX = new AuthorList( + List.of(BANU_MOSA, CORRADO_BOHM)); public static int size(String bibtex) { return AuthorList.parse(bibtex).getNumberOfAuthors(); } - private static AuthorList emptyAuthor() { - return AuthorList.parse(""); - } - - private static AuthorList oneAuthorWithLatex() { - return AuthorList.parse(MUHAMMAD_ALKHWARIZMI); - } - - private static AuthorList twoAuthorsWithLatex() { - return AuthorList.parse(MUHAMMAD_ALKHWARIZMI + " and " + CORRADO_BOHM); - } - - private static AuthorList threeAuthorsWithLatex() { - return AuthorList.parse(MUHAMMAD_ALKHWARIZMI + " and " + CORRADO_BOHM + " and " + KURT_GODEL); - } - - private static AuthorList oneInstitutionWithLatex() { - return AuthorList.parse(BANU_MOSA); - } - - private static AuthorList oneInstitutionWithParanthesisAtStart() { - return AuthorList.parse("{{\\L{}}ukasz Micha\\l{}}"); - } - - private static AuthorList twoInstitutionsWithLatex() { - return AuthorList.parse(BANU_MOSA + " and " + BANU_MOSA); - } - - private static AuthorList mixedAuthorAndInstituteWithLatex() { - return AuthorList.parse(BANU_MOSA + " and " + CORRADO_BOHM); - } - @Test public void testFixAuthorNatbib() { assertEquals("", AuthorList.fixAuthorNatbib("")); @@ -76,55 +62,55 @@ public void testFixAuthorNatbib() { @Test public void getAsNatbibLatexFreeEmptyAuthorStringForEmptyInput() { - assertEquals("", emptyAuthor().getAsNatbibLatexFree()); + assertEquals("", EMPTY_AUTHOR.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeCachesLatexFreeString() { - String cachedString = oneAuthorWithLatex().getAsNatbibLatexFree(); - assertSame(cachedString, oneAuthorWithLatex().getAsNatbibLatexFree()); + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsNatbibLatexFree(); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeUnicodeOneAuthorNameFromLatex() { assertEquals("al-Khwārizmī", - oneAuthorWithLatex().getAsNatbibLatexFree()); + ONE_AUTHOR_WITH_LATEX.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeUnicodeTwoAuthorNamesFromLatex() { assertEquals("al-Khwārizmī and Böhm", - twoAuthorsWithLatex().getAsNatbibLatexFree()); + TWO_AUTHORS_WITH_LATEX.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeUnicodeAuthorEtAlFromLatex() { assertEquals("al-Khwārizmī et al.", - threeAuthorsWithLatex().getAsNatbibLatexFree()); + THREE_AUTHORS_WITH_LATEX.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeUnicodeOneInsitutionNameFromLatex() { assertEquals("The Banū Mūsā brothers", - oneInstitutionWithLatex().getAsNatbibLatexFree()); + ONE_INSTITUTION_WITH_LATEX.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeUnicodeTwoInsitutionNameFromLatex() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsNatbibLatexFree()); + TWO_INSTITUTIONS_WITH_LATEX.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeUnicodeMixedAuthorsFromLatex() { assertEquals("The Banū Mūsā brothers and Böhm", - mixedAuthorAndInstituteWithLatex().getAsNatbibLatexFree()); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsNatbibLatexFree()); } @Test public void getAsNatbibLatexFreeOneInstitutionWithParanthesisAtStart() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsNatbibLatexFree()); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsNatbibLatexFree()); } @Test @@ -201,108 +187,120 @@ public void testFixAuthorFirstNameFirstCommas() { } @Test - public void getAsFirstLastNamesLatexFreeEmptyAuthorStringForEmptyInputAbbr() { - assertEquals("", emptyAuthor().getAsFirstLastNamesLatexFree(true, false)); + public void getAsFirstLastNamesLatexFreeEmptyAuthorStringForEmptyInputAbbreviate() { + assertEquals("", EMPTY_AUTHOR.getAsFirstLastNamesLatexFree(true, false)); } @Test - public void getAsFirstLastNamesLatexFreeCachesLatexFreeStringAbbr() { - String cachedString = oneAuthorWithLatex().getAsFirstLastNamesLatexFree(true, false); - assertSame(cachedString, oneAuthorWithLatex().getAsFirstLastNamesLatexFree(true, false)); + public void getAsFirstLastNamesLatexFreeCachesLatexFreeStringAbbreviate() { + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false)); } @Test - public void getAsFirstLastNamesLatexFreeUnicodeOneAuthorNameFromLatexAbbr() { + public void getAsFirstLastNamesLatexFreeUnicodeOneAuthorNameFromLatexAbbreviate() { assertEquals("M. al-Khwārizmī", - oneAuthorWithLatex().getAsFirstLastNamesLatexFree(true, false)); + ONE_AUTHOR_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false)); + } + + @Test + public void getAsFirstLastNamesLatexFreeUnicodeTwoAuthorNamesFromLatexAbbreviate() { + assertEquals("M. al-Khwārizmī and C. Böhm", + TWO_AUTHORS_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false)); } @Test - public void getAsFirstLastNamesLatexFreeUnicodeTwoAuthorNamesFromLatexAbbr() { + public void getAsFirstLastNamesLatexFreeUnicodeTwoAuthorNamesFromLatexAbbreviateAndOxfordComma() { assertEquals("M. al-Khwārizmī and C. Böhm", - twoAuthorsWithLatex().getAsFirstLastNamesLatexFree(true, false)); + TWO_AUTHORS_WITH_LATEX.getAsFirstLastNamesLatexFree(true, true)); } @Test - public void getAsFirstLastNamesLatexFreeThreeUnicodeAuthorsFromLatexAbbr() { + public void getAsFirstLastNamesLatexFreeThreeUnicodeAuthorsFromLatexAbbreviate() { assertEquals("M. al-Khwārizmī, C. Böhm and K. Gödel", - threeAuthorsWithLatex().getAsFirstLastNamesLatexFree(true, false)); + THREE_AUTHORS_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false)); } @Test - public void getAsFirstLastNamesLatexFreeUnicodeOneInsitutionNameFromLatexAbbr() { - assertEquals("The Banū Mūsā brothers", oneInstitutionWithLatex().getAsFirstLastNamesLatexFree(true, false)); + public void getAsFirstLastNamesLatexFreeThreeUnicodeAuthorsFromLatexAbbreviateAndOxfordComma() { + assertEquals("M. al-Khwārizmī, C. Böhm, and K. Gödel", + THREE_AUTHORS_WITH_LATEX.getAsFirstLastNamesLatexFree(true, true)); } @Test - public void getAsFirstLastNamesLatexFreeUnicodeTwoInsitutionNameFromLatexAbbr() { + public void getAsFirstLastNamesLatexFreeUnicodeOneInsitutionNameFromLatexAbbreviate() { + assertEquals("The Banū Mūsā brothers", ONE_INSTITUTION_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false)); + } + + @Test + public void getAsFirstLastNamesLatexFreeUnicodeTwoInsitutionNameFromLatexAbbreviate() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsFirstLastNamesLatexFree(true, false)); + TWO_INSTITUTIONS_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false)); } @Test - public void getAsFirstLastNamesLatexFreeUnicodeMixedAuthorsFromLatexAbbr() { + public void getAsFirstLastNamesLatexFreeUnicodeMixedAuthorsFromLatexAbbreviate() { assertEquals("The Banū Mūsā brothers and C. Böhm", - mixedAuthorAndInstituteWithLatex().getAsFirstLastNamesLatexFree(true, false)); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsFirstLastNamesLatexFree(true, false)); } @Test - public void getAsFirstLastNamesLatexFreeOneInstitutionWithParanthesisAtStartAbbr() { + public void getAsFirstLastNamesLatexFreeOneInstitutionWithParanthesisAtStartAbbreviate() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsFirstLastNamesLatexFree(true, false)); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsFirstLastNamesLatexFree(true, false)); } @Test public void getAsFirstLastNamesLatexFreeEmptyAuthorStringForEmptyInput() { - assertEquals("", emptyAuthor().getAsFirstLastNamesLatexFree(false, false)); + assertEquals("", EMPTY_AUTHOR.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeCachesLatexFreeString() { - String cachedString = oneAuthorWithLatex().getAsFirstLastNamesLatexFree(false, false); - assertSame(cachedString, oneAuthorWithLatex().getAsFirstLastNamesLatexFree(false, false)); + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeUnicodeOneAuthorNameFromLatex() { assertEquals("Muḥammad al-Khwārizmī", - oneAuthorWithLatex().getAsFirstLastNamesLatexFree(false, false)); + ONE_AUTHOR_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeUnicodeTwoAuthorNamesFromLatex() { assertEquals("Muḥammad al-Khwārizmī and Corrado Böhm", - twoAuthorsWithLatex().getAsFirstLastNamesLatexFree(false, false)); + TWO_AUTHORS_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeThreeUnicodeAuthorsFromLatex() { assertEquals("Muḥammad al-Khwārizmī, Corrado Böhm and Kurt Gödel", - threeAuthorsWithLatex().getAsFirstLastNamesLatexFree(false, false)); + THREE_AUTHORS_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeUnicodeOneInsitutionNameFromLatex() { assertEquals("The Banū Mūsā brothers", - oneInstitutionWithLatex().getAsFirstLastNamesLatexFree(false, false)); + ONE_INSTITUTION_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeUnicodeTwoInsitutionNameFromLatex() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsFirstLastNamesLatexFree(false, false)); + TWO_INSTITUTIONS_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeUnicodeMixedAuthorsFromLatex() { assertEquals("The Banū Mūsā brothers and Corrado Böhm", - mixedAuthorAndInstituteWithLatex().getAsFirstLastNamesLatexFree(false, false)); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsFirstLastNamesLatexFree(false, false)); } @Test public void getAsFirstLastNamesLatexFreeOneInstitutionWithParanthesisAtStart() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsFirstLastNamesLatexFree(false, false)); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsFirstLastNamesLatexFree(false, false)); } @Test @@ -393,214 +391,214 @@ public void testFixAuthorLastNameFirstCommasOxfordComma() { @Test public void getAsLastFirstNamesLatexFreeEmptyAuthorStringForEmptyInputAbbr() { - assertEquals("", emptyAuthor().getAsLastFirstNamesLatexFree(true, false)); + assertEquals("", EMPTY_AUTHOR.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeCachesLatexFreeStringAbbr() { - String cachedString = oneAuthorWithLatex().getAsLastFirstNamesLatexFree(true, false); - assertSame(cachedString, oneAuthorWithLatex().getAsLastFirstNamesLatexFree(true, false)); + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneAuthorNameFromLatexAbbr() { assertEquals("al-Khwārizmī, M.", - oneAuthorWithLatex().getAsLastFirstNamesLatexFree(true, false)); + ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoAuthorNamesFromLatexAbbr() { assertEquals("al-Khwārizmī, M. and Böhm, C.", - twoAuthorsWithLatex().getAsLastFirstNamesLatexFree(true, false)); + TWO_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeThreeUnicodeAuthorsFromLatexAbbr() { assertEquals("al-Khwārizmī, M., Böhm, C. and Gödel, K.", - threeAuthorsWithLatex().getAsLastFirstNamesLatexFree(true, false)); + THREE_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneInsitutionNameFromLatexAbbr() { assertEquals("The Banū Mūsā brothers", - oneInstitutionWithLatex().getAsLastFirstNamesLatexFree(true, false)); + ONE_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoInsitutionNameFromLatexAbbr() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsLastFirstNamesLatexFree(true, false)); + TWO_INSTITUTIONS_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeMixedAuthorsFromLatexAbbr() { assertEquals("The Banū Mūsā brothers and Böhm, C.", - mixedAuthorAndInstituteWithLatex().getAsLastFirstNamesLatexFree(true, false)); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeOneInstitutionWithParanthesisAtStartAbbr() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsLastFirstNamesLatexFree(true, false)); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsLastFirstNamesLatexFree(true, false)); } @Test public void getAsLastFirstNamesLatexFreeEmptyAuthorStringForEmptyInput() { - assertEquals("", emptyAuthor().getAsLastFirstNamesLatexFree(false, false)); + assertEquals("", EMPTY_AUTHOR.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeCachesLatexFreeString() { - String cachedString = oneAuthorWithLatex().getAsLastFirstNamesLatexFree(false, false); - assertSame(cachedString, oneAuthorWithLatex().getAsLastFirstNamesLatexFree(false, false)); + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneAuthorNameFromLatex() { assertEquals("al-Khwārizmī, Muḥammad", - oneAuthorWithLatex().getAsLastFirstNamesLatexFree(false, false)); + ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoAuthorNamesFromLatex() { assertEquals("al-Khwārizmī, Muḥammad and Böhm, Corrado", - twoAuthorsWithLatex().getAsLastFirstNamesLatexFree(false, false)); + TWO_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeThreeUnicodeAuthorsFromLatex() { assertEquals("al-Khwārizmī, Muḥammad, Böhm, Corrado and Gödel, Kurt", - threeAuthorsWithLatex().getAsLastFirstNamesLatexFree(false, false)); + THREE_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneInsitutionNameFromLatex() { assertEquals("The Banū Mūsā brothers", - oneInstitutionWithLatex().getAsLastFirstNamesLatexFree(false, false)); + ONE_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoInsitutionNameFromLatex() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsLastFirstNamesLatexFree(false, false)); + TWO_INSTITUTIONS_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeMixedAuthorsFromLatex() { assertEquals("The Banū Mūsā brothers and Böhm, Corrado", - mixedAuthorAndInstituteWithLatex().getAsLastFirstNamesLatexFree(false, false)); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeOneInstitutionWithParanthesisAtStart() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsLastFirstNamesLatexFree(false, false)); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsLastFirstNamesLatexFree(false, false)); } @Test public void getAsLastFirstNamesLatexFreeEmptyAuthorStringForEmptyInputAbbrOxfordComma() { - assertEquals("", emptyAuthor().getAsLastFirstNamesLatexFree(true, true)); + assertEquals("", EMPTY_AUTHOR.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeCachesLatexFreeStringAbbrOxfordComma() { - String cachedString = oneAuthorWithLatex().getAsLastFirstNamesLatexFree(true, true); - assertSame(cachedString, oneAuthorWithLatex().getAsLastFirstNamesLatexFree(true, true)); + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneAuthorNameFromLatexAbbrOxfordComma() { assertEquals("al-Khwārizmī, M.", - oneAuthorWithLatex().getAsLastFirstNamesLatexFree(true, true)); + ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoAuthorNamesFromLatexAbbrOxfordComma() { assertEquals("al-Khwārizmī, M. and Böhm, C.", - twoAuthorsWithLatex().getAsLastFirstNamesLatexFree(true, true)); + TWO_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeThreeUnicodeAuthorsFromLatexAbbrOxfordComma() { assertEquals("al-Khwārizmī, M., Böhm, C., and Gödel, K.", - threeAuthorsWithLatex().getAsLastFirstNamesLatexFree(true, true)); + THREE_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneInsitutionNameFromLatexAbbrOxfordComma() { assertEquals("The Banū Mūsā brothers", - oneInstitutionWithLatex().getAsLastFirstNamesLatexFree(true, true)); + ONE_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoInsitutionNameFromLatexAbbrOxfordComma() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsLastFirstNamesLatexFree(true, true)); + TWO_INSTITUTIONS_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeMixedAuthorsFromLatexAbbrOxfordComma() { assertEquals("The Banū Mūsā brothers and Böhm, C.", - mixedAuthorAndInstituteWithLatex().getAsLastFirstNamesLatexFree(true, true)); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeOneInstitutionWithParanthesisAtStartAbbrOxfordComma() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsLastFirstNamesLatexFree(true, true)); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsLastFirstNamesLatexFree(true, true)); } @Test public void getAsLastFirstNamesLatexFreeEmptyAuthorStringForEmptyInputOxfordComma() { - assertEquals("", emptyAuthor().getAsLastFirstNamesLatexFree(false, true)); + assertEquals("", EMPTY_AUTHOR.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeCachesLatexFreeStringOxfordComma() { - String cachedString = oneAuthorWithLatex().getAsLastFirstNamesLatexFree(false, true); - assertSame(cachedString, oneAuthorWithLatex().getAsLastFirstNamesLatexFree(false, true)); + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneAuthorNameFromLatexOxfordComma() { assertEquals("al-Khwārizmī, Muḥammad", - oneAuthorWithLatex().getAsLastFirstNamesLatexFree(false, true)); + ONE_AUTHOR_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoAuthorNamesFromLatexOxfordComma() { assertEquals("al-Khwārizmī, Muḥammad and Böhm, Corrado", - twoAuthorsWithLatex().getAsLastFirstNamesLatexFree(false, true)); + TWO_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeThreeUnicodeAuthorsFromLatexOxfordComma() { assertEquals("al-Khwārizmī, Muḥammad, Böhm, Corrado, and Gödel, Kurt", - threeAuthorsWithLatex().getAsLastFirstNamesLatexFree(false, true)); + THREE_AUTHORS_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeOneInsitutionNameFromLatexOxfordComma() { assertEquals("The Banū Mūsā brothers", - oneInstitutionWithLatex().getAsLastFirstNamesLatexFree(false, true)); + ONE_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeTwoInsitutionNameFromLatexOxfordComma() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsLastFirstNamesLatexFree(false, true)); + TWO_INSTITUTIONS_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeUnicodeMixedAuthorsFromLatexOxfordComma() { assertEquals("The Banū Mūsā brothers and Böhm, Corrado", - mixedAuthorAndInstituteWithLatex().getAsLastFirstNamesLatexFree(false, true)); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsLastFirstNamesLatexFree(false, true)); } @Test public void getAsLastFirstNamesLatexFreeOneInstitutionWithParanthesisAtStartOxfordComma() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsLastFirstNamesLatexFree(false, true)); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsLastFirstNamesLatexFree(false, true)); } @Test @@ -689,46 +687,56 @@ public void testFixAuthorLastNameOnlyCommas() { @Test public void getAsLastNamesLatexFreeCachesLatexFreeString() { - String cachedString = oneAuthorWithLatex().getAsLastNamesLatexFree(false); - assertSame(cachedString, oneAuthorWithLatex().getAsLastNamesLatexFree(false)); + String cachedString = ONE_AUTHOR_WITH_LATEX.getAsLastNamesLatexFree(false); + assertSame(cachedString, ONE_AUTHOR_WITH_LATEX.getAsLastNamesLatexFree(false)); } @Test public void getAsLastNamesLatexFreeUnicodeOneAuthorNameFromLatex() { - assertEquals("al-Khwārizmī", oneAuthorWithLatex().getAsLastNamesLatexFree(false)); + assertEquals("al-Khwārizmī", ONE_AUTHOR_WITH_LATEX.getAsLastNamesLatexFree(false)); } @Test public void getAsLastNamesLatexFreeUnicodeTwoAuthorNamesFromLatex() { - assertEquals("al-Khwārizmī and Böhm", twoAuthorsWithLatex().getAsLastNamesLatexFree(false)); + assertEquals("al-Khwārizmī and Böhm", TWO_AUTHORS_WITH_LATEX.getAsLastNamesLatexFree(false)); + } + + @Test + public void getAsLastNamesLatexFreeUnicodeTwoAuthorNamesFromLatexUsingOxfordComma() { + assertEquals("al-Khwārizmī and Böhm", TWO_AUTHORS_WITH_LATEX.getAsLastNamesLatexFree(true)); + } + + @Test + public void getAsLastNamesLatexFreeUnicodeThreeAuthorsFromLatex() { + assertEquals("al-Khwārizmī, Böhm and Gödel", THREE_AUTHORS_WITH_LATEX.getAsLastNamesLatexFree(false)); } @Test - public void getAsLastNamesLatexFreeUnicodeThreeUnicodeAuthorsFromLatex() { - assertEquals("al-Khwārizmī, Böhm and Gödel", threeAuthorsWithLatex().getAsLastNamesLatexFree(false)); + public void getAsLastNamesLatexFreeUnicodeThreeAuthorsFromLatexUsingOxfordComma() { + assertEquals("al-Khwārizmī, Böhm, and Gödel", THREE_AUTHORS_WITH_LATEX.getAsLastNamesLatexFree(true)); } @Test public void getAsLastNamesLatexFreeUnicodeOneInsitutionNameFromLatex() { - assertEquals("The Banū Mūsā brothers", oneInstitutionWithLatex().getAsLastNamesLatexFree(false)); + assertEquals("The Banū Mūsā brothers", ONE_INSTITUTION_WITH_LATEX.getAsLastNamesLatexFree(false)); } @Test public void getAsLastNamesLatexFreeUnicodeTwoInsitutionNameFromLatex() { assertEquals("The Banū Mūsā brothers and The Banū Mūsā brothers", - twoInstitutionsWithLatex().getAsLastNamesLatexFree(false)); + TWO_INSTITUTIONS_WITH_LATEX.getAsLastNamesLatexFree(false)); } @Test public void getAsLastNamesLatexFreeUnicodeMixedAuthorsFromLatex() { assertEquals("The Banū Mūsā brothers and Böhm", - mixedAuthorAndInstituteWithLatex().getAsLastNamesLatexFree(false)); + MIXED_AUTHOR_AND_INSTITUTION_WITH_LATEX.getAsLastNamesLatexFree(false)); } @Test public void getAsLastNamesLatexFreeOneInstitutionWithParanthesisAtStart() { assertEquals("Łukasz Michał", - oneInstitutionWithParanthesisAtStart().getAsLastNamesLatexFree(false)); + ONE_INSTITUTION_WITH_STARTING_PARANTHESIS.getAsLastNamesLatexFree(false)); } @Test @@ -1108,31 +1116,84 @@ public void parseNameWithBraces() throws Exception { @Test public void parseFirstNameFromFirstAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("Mu{\\d{h}}ammad", - twoAuthorsWithLatex().getAuthor(0).getFirst().orElse(null)); + AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") + .getAuthor(0).getFirst().orElse(null)); } @Test public void parseFirstNameFromSecondAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("Corrado", - twoAuthorsWithLatex().getAuthor(1).getFirst().orElse(null)); + AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") + .getAuthor(1).getFirst().orElse(null)); } @Test public void parseLastNameFromFirstAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("al-Khw{\\={a}}rizm{\\={i}}", - twoAuthorsWithLatex().getAuthor(0).getLast().orElse(null)); + AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") + .getAuthor(0).getLast().orElse(null)); } @Test public void parseLastNameFromSecondAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("B{\\\"o}hm", - twoAuthorsWithLatex().getAuthor(1).getLast().orElse(null)); + AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") + .getAuthor(1).getLast().orElse(null)); } @Test public void parseInstitutionAuthorWithLatexNames() throws Exception { assertEquals("The Ban\\={u} M\\={u}s\\={a} brothers", - oneInstitutionWithLatex().getAuthor(0).getLast().orElse(null)); + AuthorList.parse("{The Ban\\={u} M\\={u}s\\={a} brothers}").getAuthor(0).getLast().orElse(null)); + } + + @Test + public void parseRetrieveCachedAuthorListAfterGarbageCollection() throws Exception { + final String uniqueAuthorName = "Osvaldo Iongi"; + AuthorList author = AuthorList.parse(uniqueAuthorName); + System.gc(); + assertSame(author, AuthorList.parse(uniqueAuthorName)); + } + + @Test + public void parseGarbageCollectAuthorListForUnreachableKey() throws Exception { + final String uniqueAuthorName = "Fleur Hornbach"; + // Note that "new String()" is needed, uniqueAuthorName is a reference to a String literal + AuthorList uniqueAuthor = AuthorList.parse(new String(uniqueAuthorName)); + System.gc(); + assertNotSame(uniqueAuthor, AuthorList.parse(uniqueAuthorName)); + } + + @Test + public void parseGarbageCollectUnreachableInstitution() throws Exception { + final String uniqueInstitutionName = "{Unique LLC}"; + // Note that "new String()" is needed, uniqueAuthorName is a reference to a String literal + AuthorList uniqueInstitution = AuthorList.parse(new String(uniqueInstitutionName)); + System.gc(); + assertNotSame(uniqueInstitution, AuthorList.parse(uniqueInstitutionName)); + } + + /** + * This tests an unreachable key issue addressed in [#6552](https://github.com/JabRef/jabref/pull/6552). + * The test is incorrect BibTeX but is handled by the parser and common in practice. + */ + @Test + public void parseCacheAuthorsWithTwoOrMoreCommasAndWithSpaceInAllParts() throws Exception { + final String uniqueAuthorsNames = "Basil Dankworth, Gianna Birdwhistle, Cosmo Berrycloth"; + AuthorList uniqueAuthors = AuthorList.parse(uniqueAuthorsNames); + System.gc(); + assertSame(uniqueAuthors, AuthorList.parse(uniqueAuthorsNames)); + } + + /** + * This tests an unreachable key issue addressed in [#6552](https://github.com/JabRef/jabref/pull/6552). + */ + @Test + public void parseCacheAuthorsWithTwoOrMoreCommasAndWithoutSpaceInAllParts() throws Exception { + final String uniqueAuthorsNames = "Dankworth, Jr., Braelynn"; + AuthorList uniqueAuthors = AuthorList.parse(uniqueAuthorsNames); + System.gc(); + assertSame(uniqueAuthors, AuthorList.parse(uniqueAuthorsNames)); } /** @@ -1152,4 +1213,70 @@ public void correctNamesWithOneComma() throws Exception { expected = new Author("José María", "J. M.", null, "Rodriguez Fernandez", null); assertEquals(new AuthorList(expected), AuthorList.parse("Rodriguez Fernandez, José María")); } + + @Test + public void equalsFalseDifferentOrder() { + Author firstAuthor = new Author("A", null, null, null, null); + Author secondAuthor = new Author("B", null, null, null, null); + AuthorList firstAuthorList = new AuthorList(List.of(firstAuthor, secondAuthor)); + AuthorList secondAuthorList = new AuthorList(List.of(secondAuthor, firstAuthor)); + assertNotEquals(firstAuthorList, secondAuthorList); + } + + @Test + public void equalsFalseWhenNotAuthorList() { + assertNotEquals(new AuthorList(new Author(null, null, null, null, null)), + new Author(null, null, null, null, null)); + } + + @Test + public void equalsTrueReflexive() { + AuthorList authorList = new AuthorList(new Author(null, null, null, null, null)); + assertEquals(authorList, authorList); + } + + @Test + public void equalsTrueSymmetric() { + AuthorList firstAuthorList = new AuthorList(new Author("A", null, null, null, null)); + AuthorList secondAuthorList = new AuthorList(new Author("A", null, null, null, null)); + assertEquals(firstAuthorList, secondAuthorList); + assertEquals(secondAuthorList, firstAuthorList); + } + + @Test + public void equalsTrueTransitive() { + AuthorList firstAuthorList = new AuthorList(new Author("A", null, null, null, null)); + AuthorList secondAuthorList = new AuthorList(new Author("A", null, null, null, null)); + AuthorList thirdAuthorList = new AuthorList(new Author("A", null, null, null, null)); + assertEquals(firstAuthorList, secondAuthorList); + assertEquals(secondAuthorList, thirdAuthorList); + assertEquals(firstAuthorList, thirdAuthorList); + } + + @Test + public void equalsTrueConsistent() { + AuthorList firstAuthorList = new AuthorList(new Author("A", null, null, null, null)); + AuthorList secondAuthorList = new AuthorList(new Author("A", null, null, null, null)); + assertEquals(firstAuthorList, secondAuthorList); + assertEquals(firstAuthorList, secondAuthorList); + assertEquals(firstAuthorList, secondAuthorList); + } + + @Test + public void equalsFalseForNull() { + assertNotEquals(null, new AuthorList(new Author(null, null, null, null, null))); + } + + @Test + public void hashCodeConsistent() { + AuthorList authorList = new AuthorList(new Author(null, null, null, null, null)); + assertEquals(authorList.hashCode(), authorList.hashCode()); + } + + @Test + public void hashCodeNotConstant() { + AuthorList firstAuthorList = new AuthorList(new Author("A", null, null, null, null)); + AuthorList secondAuthorList = new AuthorList(new Author("B", null, null, null, null)); + assertNotEquals(firstAuthorList.hashCode(), secondAuthorList.hashCode()); + } }