From e82f01e90b6d2d9530cd3f01d6ec5fe668b0d232 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Tue, 22 Dec 2020 15:02:12 -0500 Subject: [PATCH 01/22] Add SearchStrategy injection --- .../java/org/jabref/model/groups/WordKeywordGroup.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/jabref/model/groups/WordKeywordGroup.java b/src/main/java/org/jabref/model/groups/WordKeywordGroup.java index 46ca9fc7e49..a0ea903dc17 100644 --- a/src/main/java/org/jabref/model/groups/WordKeywordGroup.java +++ b/src/main/java/org/jabref/model/groups/WordKeywordGroup.java @@ -46,6 +46,14 @@ public WordKeywordGroup(String name, GroupHierarchyType context, Field searchFie } } + public WordKeywordGroup(String name, GroupHierarchyType context, Field searchField, + String searchExpression, boolean caseSensitive, Character keywordSeparator, SearchStrategy searchStrategy) { + super(name, context, searchField, searchExpression, caseSensitive); + this.keywordSeparator = keywordSeparator; + onlySplitWordsAtSeparator = false; + this.searchStrategy = searchStrategy; + } + private static boolean containsCaseInsensitive(Set searchIn, Collection searchFor) { for (String searchWord : searchFor) { if (!containsCaseInsensitive(searchIn, searchWord)) { From 0117f9dc44ebd7f7d16f9addf77552cae7483e62 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Tue, 22 Dec 2020 15:03:13 -0500 Subject: [PATCH 02/22] Fix caching, parsing and matching last names --- .../model/groups/AutomaticPersonsGroup.java | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java index f113ed38ad8..2184fdfe8c2 100644 --- a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java +++ b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java @@ -1,5 +1,9 @@ package org.jabref.model.groups; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -9,11 +13,12 @@ import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; -import org.jabref.model.util.OptionalUtil; +import org.jabref.model.strings.LatexToUnicodeAdapter; public class AutomaticPersonsGroup extends AutomaticGroup { - private Field field; + private static final Map> CACHED_LASTNAMES = new HashMap<>(); + private final Field field; public AutomaticPersonsGroup(String name, GroupHierarchyType context, Field field) { super(name, context); @@ -44,16 +49,48 @@ public AbstractGroup deepCopy() { @Override public Set createSubgroups(BibEntry entry) { - Optional authorList = entry.getLatexFreeField(field) - .map(AuthorList::parse); - return OptionalUtil.flatMap(authorList, AuthorList::getAuthors) - .map(Author::getLast) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(lastName -> !lastName.isEmpty()) - .map(lastName -> new WordKeywordGroup(lastName, GroupHierarchyType.INDEPENDENT, field, lastName, true, ' ', true)) - .map(GroupTreeNode::new) - .collect(Collectors.toSet()); + return getAsLastNamesLatexFree(field, entry).stream() + .map(lastName -> new WordKeywordGroup(lastName, GroupHierarchyType.INDEPENDENT, + field, lastName, true, ' ', new LastNameSearchStrategy(lastName, field))) + .map(GroupTreeNode::new) + .collect(Collectors.toSet()); + } + + private static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { + final String unparsedAuthorList = bibEntry.getField(field).orElse(null); + List lastNames = CACHED_LASTNAMES.get(unparsedAuthorList); + if (lastNames != null) { + return lastNames; + } + + lastNames = bibEntry.getField(field) + .map(AuthorList::parse) + .map(AuthorList::getAuthors) + .map(authors -> + authors.stream() + .map(Author::getLast) + .flatMap(Optional::stream) + .map(LatexToUnicodeAdapter::format) + .collect(Collectors.toList())) + .orElse(Collections.emptyList()); + + CACHED_LASTNAMES.put(unparsedAuthorList, lastNames); + return lastNames; + } + + private static class LastNameSearchStrategy implements WordKeywordGroup.SearchStrategy { + private final Field field; + private final String lastName; + + public LastNameSearchStrategy(String lastName, Field field) { + this.field = field; + this.lastName = lastName; + } + + @Override + public boolean contains(BibEntry entry) { + return getAsLastNamesLatexFree(field, entry).stream().anyMatch(name -> name.equals(lastName)); + } } public Field getField() { From cc187ee8ba7e708b6fbbff99e9c01d12e60d91c0 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Tue, 29 Dec 2020 09:27:12 -0500 Subject: [PATCH 03/22] Add test cases --- .../groups/AutomaticPersonsGroupTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java diff --git a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java new file mode 100644 index 00000000000..b066e2696d9 --- /dev/null +++ b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java @@ -0,0 +1,51 @@ +package org.jabref.model.groups; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; + +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.assertTrue; + +class AutomaticPersonsGroupTest { + @Test + void createSubgroupsFromCommaSeparatedLastNames() { + BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan and Hopper, Grace"); + var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); + assertEquals(2, subgroups.size()); + for (GroupTreeNode group : subgroups) { + String groupName = group.getGroup().getName(); + assertTrue(groupName.equals("Turing") || groupName.equals("Hopper")); + } + } + + @Test + void createSubgroupsContainingCommaSeparatedLastNames() { + BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan and Hopper, Grace"); + BibEntry turingEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan"); + BibEntry hopperEntry = new BibEntry().withField(StandardField.AUTHOR, "Hopper, Grace"); + var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); + for (GroupTreeNode group : subgroups) { + String groupName = group.getGroup().getName(); + if (groupName.equals("Turing")) { + assertTrue(group.matches(turingEntry)); + assertFalse(group.matches(hopperEntry)); + } else if (groupName.equals("Hopper")) { + assertTrue(group.matches(hopperEntry)); + assertFalse(group.matches(turingEntry)); + } + } + } + + @Test + void createSubgroupContainingLatexAndUnicodeLastNames() { + BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt G{\\\"{o}}del"); + BibEntry godelEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt Gödel"); + var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); + for (GroupTreeNode group : subgroups) { // There should only be one subgroup + assertTrue(group.matches(godelEntry)); + } + } +} From f87503ac3a9baa88a8ef02cec3ecbe20da608d39 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Tue, 29 Dec 2020 12:30:02 -0500 Subject: [PATCH 04/22] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ac0262ca58..bca60fa730a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where the password for a shared SQL database was not remembered [#6869](https://github.com/JabRef/jabref/issues/6869) - We fixed an issue where newly added entires were not synced to a shared SQL database [#7176](https://github.com/JabRef/jabref/issues/7176) - We fixed an issue where the PDF-Content importer threw an exception when no DOI number is present at the first page of the PDF document [#7203](https://github.com/JabRef/jabref/issues/7203) +- We fixed an issue where groups generated from authors' last names did not include all entries of the authors' [#5833](https://github.com/JabRef/jabref/issues/5833) ### Removed From de08b356fe19698f4cb29825ae8bd298eae893d5 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:02:34 -0500 Subject: [PATCH 05/22] Add LastNameGroup as a standalone group --- .../model/groups/AutomaticPersonsGroup.java | 27 +++--------- .../jabref/model/groups/LastNameGroup.java | 41 +++++++++++++++++++ .../jabref/model/groups/WordKeywordGroup.java | 8 ---- 3 files changed, 47 insertions(+), 29 deletions(-) create mode 100644 src/main/java/org/jabref/model/groups/LastNameGroup.java diff --git a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java index 2184fdfe8c2..44924a57f48 100644 --- a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java +++ b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java @@ -49,14 +49,14 @@ public AbstractGroup deepCopy() { @Override public Set createSubgroups(BibEntry entry) { - return getAsLastNamesLatexFree(field, entry).stream() - .map(lastName -> new WordKeywordGroup(lastName, GroupHierarchyType.INDEPENDENT, - field, lastName, true, ' ', new LastNameSearchStrategy(lastName, field))) - .map(GroupTreeNode::new) - .collect(Collectors.toSet()); + return getAsLastNamesLatexFree(field, entry) + .stream() + .map(lastName -> new LastNameGroup(lastName, GroupHierarchyType.INDEPENDENT, field, lastName)) + .map(GroupTreeNode::new) + .collect(Collectors.toSet()); } - private static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { + static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { final String unparsedAuthorList = bibEntry.getField(field).orElse(null); List lastNames = CACHED_LASTNAMES.get(unparsedAuthorList); if (lastNames != null) { @@ -78,21 +78,6 @@ private static List getAsLastNamesLatexFree(Field field, BibEntry bibEnt return lastNames; } - private static class LastNameSearchStrategy implements WordKeywordGroup.SearchStrategy { - private final Field field; - private final String lastName; - - public LastNameSearchStrategy(String lastName, Field field) { - this.field = field; - this.lastName = lastName; - } - - @Override - public boolean contains(BibEntry entry) { - return getAsLastNamesLatexFree(field, entry).stream().anyMatch(name -> name.equals(lastName)); - } - } - public Field getField() { return field; } diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java new file mode 100644 index 00000000000..6ec77850289 --- /dev/null +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -0,0 +1,41 @@ +package org.jabref.model.groups; + +import java.util.Objects; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.Field; + +/** + * Matches entries based on the last name of a list of authors in a specified field. + */ +public class LastNameGroup extends KeywordGroup { + public LastNameGroup(String name, GroupHierarchyType context, Field searchField, String searchExpression) { + super(name, context, searchField, searchExpression, true); + } + + @Override + public boolean contains(BibEntry entry) { + return AutomaticPersonsGroup.getAsLastNamesLatexFree(searchField, entry) + .stream().anyMatch(name -> name.equals(searchExpression)); + } + + @Override + public AbstractGroup deepCopy() { + return new LastNameGroup(getName(), context, searchField, searchExpression); + } + + @Override + public boolean equals(Object other) { + if (super.equals(other)) { + LastNameGroup otherGroup = (LastNameGroup) other; + return (this.searchField.equals(otherGroup.searchField) && + this.searchExpression.equals(otherGroup.searchExpression)); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), searchField, searchExpression); + } +} diff --git a/src/main/java/org/jabref/model/groups/WordKeywordGroup.java b/src/main/java/org/jabref/model/groups/WordKeywordGroup.java index a0ea903dc17..46ca9fc7e49 100644 --- a/src/main/java/org/jabref/model/groups/WordKeywordGroup.java +++ b/src/main/java/org/jabref/model/groups/WordKeywordGroup.java @@ -46,14 +46,6 @@ public WordKeywordGroup(String name, GroupHierarchyType context, Field searchFie } } - public WordKeywordGroup(String name, GroupHierarchyType context, Field searchField, - String searchExpression, boolean caseSensitive, Character keywordSeparator, SearchStrategy searchStrategy) { - super(name, context, searchField, searchExpression, caseSensitive); - this.keywordSeparator = keywordSeparator; - onlySplitWordsAtSeparator = false; - this.searchStrategy = searchStrategy; - } - private static boolean containsCaseInsensitive(Set searchIn, Collection searchFor) { for (String searchWord : searchFor) { if (!containsCaseInsensitive(searchIn, searchWord)) { From b2a9fe92c03b81741767756707af1e32306bade6 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:21:31 -0500 Subject: [PATCH 06/22] Fix test cases --- .../groups/AutomaticPersonsGroupTest.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java index b066e2696d9..c1bd6642fe1 100644 --- a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java +++ b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java @@ -1,24 +1,32 @@ package org.jabref.model.groups; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; 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.assertTrue; class AutomaticPersonsGroupTest { + private static Set createPersonSubGroupFrom(String... lastNames) { + return Arrays.stream(lastNames).distinct() + .map(lastName -> + new LastNameGroup(lastName, GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR, lastName)) + .map(GroupTreeNode::new) + .collect(Collectors.toSet()); + } + @Test void createSubgroupsFromCommaSeparatedLastNames() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan and Hopper, Grace"); var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); - assertEquals(2, subgroups.size()); - for (GroupTreeNode group : subgroups) { - String groupName = group.getGroup().getName(); - assertTrue(groupName.equals("Turing") || groupName.equals("Hopper")); - } + var expectedSubgroups = createPersonSubGroupFrom("Turing", "Hopper"); + assertEquals(expectedSubgroups, subgroups); } @Test @@ -27,24 +35,16 @@ void createSubgroupsContainingCommaSeparatedLastNames() { BibEntry turingEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan"); BibEntry hopperEntry = new BibEntry().withField(StandardField.AUTHOR, "Hopper, Grace"); var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); - for (GroupTreeNode group : subgroups) { - String groupName = group.getGroup().getName(); - if (groupName.equals("Turing")) { - assertTrue(group.matches(turingEntry)); - assertFalse(group.matches(hopperEntry)); - } else if (groupName.equals("Hopper")) { - assertTrue(group.matches(hopperEntry)); - assertFalse(group.matches(turingEntry)); - } - } + var expectedSubgroups = createPersonSubGroupFrom("Turing", "Hopper"); + assertEquals(expectedSubgroups, subgroups); } @Test void createSubgroupContainingLatexAndUnicodeLastNames() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt G{\\\"{o}}del"); BibEntry godelEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt Gödel"); - var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); - for (GroupTreeNode group : subgroups) { // There should only be one subgroup + var subgroup = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); + for (GroupTreeNode group : subgroup) { // There should only be one subgroup assertTrue(group.matches(godelEntry)); } } From 7566fa48cc2d88a2c318fea3dac4b63e1dd95fbe Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:43:40 -0500 Subject: [PATCH 07/22] Fix checkstyle --- .../java/org/jabref/model/entry/Author.java | 80 ++++++------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/jabref/model/entry/Author.java b/src/main/java/org/jabref/model/entry/Author.java index b325a3fa92f..7520cbb3994 100644 --- a/src/main/java/org/jabref/model/entry/Author.java +++ b/src/main/java/org/jabref/model/entry/Author.java @@ -6,13 +6,9 @@ import org.jabref.model.strings.StringUtil; /** - * This is an immutable class that keeps information regarding single - * author. It is just a container for the information, with very simple - * methods to access it. + * This is an immutable class that keeps information regarding single author. It is just a container for the information, with very simple methods to access it. *

- * Current usage: only methods getLastOnly, - * getFirstLast, and getLastFirst are used; - * all other methods are provided for completeness. + * Current usage: only methods getLastOnly, getFirstLast, and getLastFirst are used; all other methods are provided for completeness. */ public class Author { @@ -27,25 +23,13 @@ public class Author { private final String jrPart; /** - * Creates the Author object. If any part of the name is absent, null - * must be passed; otherwise other methods may return erroneous results. + * Creates the Author object. If any part of the name is absent, null must be passed; otherwise other methods may return erroneous results. * - * @param first the first name of the author (may consist of several - * tokens, like "Charles Louis Xavier Joseph" in "Charles - * Louis Xavier Joseph de la Vall{\'e}e Poussin") - * @param firstabbr the abbreviated first name of the author (may consist of - * several tokens, like "C. L. X. J." in "Charles Louis - * Xavier Joseph de la Vall{\'e}e Poussin"). It is a - * responsibility of the caller to create a reasonable - * abbreviation of the first name. - * @param von the von part of the author's name (may consist of several - * tokens, like "de la" in "Charles Louis Xavier Joseph de la - * Vall{\'e}e Poussin") - * @param last the last name of the author (may consist of several - * tokens, like "Vall{\'e}e Poussin" in "Charles Louis Xavier - * Joseph de la Vall{\'e}e Poussin") - * @param jr the junior part of the author's name (may consist of - * several tokens, like "Jr. III" in "Smith, Jr. III, John") + * @param first the first name of the author (may consist of several tokens, like "Charles Louis Xavier Joseph" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") + * @param firstabbr the abbreviated first name of the author (may consist of several tokens, like "C. L. X. J." in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin"). It is a responsibility of the caller to create a reasonable abbreviation of the first name. + * @param von the von part of the author's name (may consist of several tokens, like "de la" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") + * @param last the last name of the author (may consist of several tokens, like "Vall{\'e}e Poussin" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") + * @param jr the junior part of the author's name (may consist of several tokens, like "Jr. III" in "Smith, Jr. III, John") */ public Author(String first, String firstabbr, String von, String last, String jr) { firstPart = addDotIfAbbreviation(removeStartAndEndBraces(first)); @@ -198,9 +182,11 @@ private boolean properBrackets(String s) { * Removes start and end brace at a string *

* E.g., - * * {Vall{\'e}e Poussin} -> Vall{\'e}e Poussin - * * {Vall{\'e}e} {Poussin} -> Vall{\'e}e Poussin - * * Vall{\'e}e Poussin -> Vall{\'e}e Poussin + *

    + *
  • {Vall{\'e}e Poussin} -> Vall{\'e}e Poussin
  • + *
  • {Vall{\'e}e} {Poussin} -> Vall{\'e}e Poussin
  • + *
  • Vall{\'e}e Poussin -> Vall{\'e}e Poussin
  • + *
*/ private String removeStartAndEndBraces(String name) { if (StringUtil.isBlank(name)) { @@ -263,19 +249,16 @@ public Optional getFirst() { } /** - * Returns the abbreviated first name of the author stored in this - * object ("F."). + * Returns the abbreviated first name of the author stored in this object ("F."). * - * @return abbreviated first name of the author (may consist of several - * tokens) + * @return abbreviated first name of the author (may consist of several tokens) */ public Optional getFirstAbbr() { return Optional.ofNullable(firstAbbr); } /** - * Returns the von part of the author's name stored in this object - * ("von"). + * Returns the von part of the author's name stored in this object ("von"). * * @return von part of the author's name (may consist of several tokens) */ @@ -296,17 +279,14 @@ public Optional getLast() { * Returns the junior part of the author's name stored in this object * ("Jr"). * - * @return junior part of the author's name (may consist of several - * tokens) or null if the author does not have a Jr. Part + * @return junior part of the author's name (may consist of several tokens) or null if the author does not have a Jr. Part */ public Optional getJr() { return Optional.ofNullable(jrPart); } /** - * Returns von-part followed by last name ("von Last"). If both fields - * were specified as null, the empty string "" - * is returned. + * Returns von-part followed by last name ("von Last"). If both fields were specified as null, the empty string "" is returned. * * @return 'von Last' */ @@ -319,13 +299,10 @@ public String getLastOnly() { } /** - * Returns the author's name in form 'von Last, Jr., First' with the - * first name full or abbreviated depending on parameter. + * Returns the author's name in form 'von Last, Jr., First' with the first name full or abbreviated depending on parameter. * - * @param abbr true - abbreviate first name, false - - * do not abbreviate - * @return 'von Last, Jr., First' (if abbr==false) or - * 'von Last, Jr., F.' (if abbr==true) + * @param abbr true - abbreviate first name, false - do not abbreviate + * @return 'von Last, Jr., First' (if abbr==false) or 'von Last, Jr., F.' (if abbr==true) */ public String getLastFirst(boolean abbr) { StringBuilder res = new StringBuilder(getLastOnly()); @@ -339,13 +316,10 @@ public String getLastFirst(boolean abbr) { } /** - * Returns the author's name in form 'First von Last, Jr.' with the - * first name full or abbreviated depending on parameter. + * Returns the author's name in form 'First von Last, Jr.' with the first name full or abbreviated depending on parameter. * - * @param abbr true - abbreviate first name, false - - * do not abbreviate - * @return 'First von Last, Jr.' (if abbr==false) or 'F. - * von Last, Jr.' (if abbr==true) + * @param abbr true - abbreviate first name, false - do not abbreviate + * @return 'First von Last, Jr.' (if abbr==false) or 'F. von Last, Jr.' (if abbr==true) */ public String getFirstLast(boolean abbr) { StringBuilder res = new StringBuilder(); @@ -372,11 +346,9 @@ public String toString() { } /** - * Returns the name as "Last, Jr, F." omitting the von-part and removing - * starting braces. + * Returns the name as "Last, Jr, F." omitting the von-part and removing starting braces. * - * @return "Last, Jr, F." as described above or "" if all these parts - * are empty. + * @return "Last, Jr, F." as described above or "" if all these parts are empty. */ public String getNameForAlphabetization() { StringBuilder res = new StringBuilder(); From 8a77d4ebc01d89bf8bb6aaaca7911889079b553b Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:44:10 -0500 Subject: [PATCH 08/22] Add caching of last name --- .../java/org/jabref/model/entry/Author.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/model/entry/Author.java b/src/main/java/org/jabref/model/entry/Author.java index 7520cbb3994..dc3a45db037 100644 --- a/src/main/java/org/jabref/model/entry/Author.java +++ b/src/main/java/org/jabref/model/entry/Author.java @@ -3,6 +3,7 @@ import java.util.Objects; import java.util.Optional; +import org.jabref.model.strings.LatexToUnicodeAdapter; import org.jabref.model.strings.StringUtil; /** @@ -13,14 +14,11 @@ public class Author { private final String firstPart; - private final String firstAbbr; - private final String vonPart; - private final String lastPart; - private final String jrPart; + private String latexFreeLastPart; /** * Creates the Author object. If any part of the name is absent, null must be passed; otherwise other methods may return erroneous results. @@ -276,8 +274,19 @@ public Optional getLast() { } /** - * Returns the junior part of the author's name stored in this object - * ("Jr"). + * Returns the last name of the author stored in this object with resolved latex. + * + * @return last name of the author (may consist of several tokens) + */ + public Optional getLastLatexFree() { + if (latexFreeLastPart == null && lastPart != null) { + latexFreeLastPart = LatexToUnicodeAdapter.format(lastPart); + } + return Optional.ofNullable(lastPart); + } + + /** + * Returns the junior part of the author's name stored in this object ("Jr"). * * @return junior part of the author's name (may consist of several tokens) or null if the author does not have a Jr. Part */ From 5a907243a07b8a8847b808b2f7fedcb6a84de7db Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:45:45 -0500 Subject: [PATCH 09/22] Fix usage of latex free last names --- .../model/groups/AutomaticPersonsGroup.java | 41 +++---------------- .../jabref/model/groups/LastNameGroup.java | 21 +++++++++- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java index 44924a57f48..bd31c03a674 100644 --- a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java +++ b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java @@ -1,23 +1,14 @@ package org.jabref.model.groups; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import org.jabref.model.entry.Author; -import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; -import org.jabref.model.strings.LatexToUnicodeAdapter; public class AutomaticPersonsGroup extends AutomaticGroup { - private static final Map> CACHED_LASTNAMES = new HashMap<>(); private final Field field; public AutomaticPersonsGroup(String name, GroupHierarchyType context, Field field) { @@ -49,33 +40,11 @@ public AbstractGroup deepCopy() { @Override public Set createSubgroups(BibEntry entry) { - return getAsLastNamesLatexFree(field, entry) - .stream() - .map(lastName -> new LastNameGroup(lastName, GroupHierarchyType.INDEPENDENT, field, lastName)) - .map(GroupTreeNode::new) - .collect(Collectors.toSet()); - } - - static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { - final String unparsedAuthorList = bibEntry.getField(field).orElse(null); - List lastNames = CACHED_LASTNAMES.get(unparsedAuthorList); - if (lastNames != null) { - return lastNames; - } - - lastNames = bibEntry.getField(field) - .map(AuthorList::parse) - .map(AuthorList::getAuthors) - .map(authors -> - authors.stream() - .map(Author::getLast) - .flatMap(Optional::stream) - .map(LatexToUnicodeAdapter::format) - .collect(Collectors.toList())) - .orElse(Collections.emptyList()); - - CACHED_LASTNAMES.put(unparsedAuthorList, lastNames); - return lastNames; + return LastNameGroup.getAsLastNamesLatexFree(field, entry) + .stream() + .map(lastName -> new LastNameGroup(lastName, GroupHierarchyType.INDEPENDENT, field, lastName)) + .map(GroupTreeNode::new) + .collect(Collectors.toSet()); } public Field getField() { diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index 6ec77850289..93880101555 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -1,7 +1,13 @@ package org.jabref.model.groups; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import org.jabref.model.entry.Author; +import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; @@ -13,10 +19,21 @@ public LastNameGroup(String name, GroupHierarchyType context, Field searchField, super(name, context, searchField, searchExpression, true); } + static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { + return bibEntry.getField(field) + .map(AuthorList::parse) + .map(AuthorList::getAuthors) + .map(authors -> + authors.stream() + .map(Author::getLastLatexFree) + .flatMap(Optional::stream) + .collect(Collectors.toList())) + .orElse(Collections.emptyList()); + } + @Override public boolean contains(BibEntry entry) { - return AutomaticPersonsGroup.getAsLastNamesLatexFree(searchField, entry) - .stream().anyMatch(name -> name.equals(searchExpression)); + return getAsLastNamesLatexFree(searchField, entry).stream().anyMatch(name -> name.equals(searchExpression)); } @Override From 8e185f92b20fde786a0e408d517115e700d78f89 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:57:11 -0500 Subject: [PATCH 10/22] Fix mistake in latex free caching --- src/main/java/org/jabref/model/entry/Author.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/model/entry/Author.java b/src/main/java/org/jabref/model/entry/Author.java index dc3a45db037..ab94d260c43 100644 --- a/src/main/java/org/jabref/model/entry/Author.java +++ b/src/main/java/org/jabref/model/entry/Author.java @@ -282,7 +282,7 @@ public Optional getLastLatexFree() { if (latexFreeLastPart == null && lastPart != null) { latexFreeLastPart = LatexToUnicodeAdapter.format(lastPart); } - return Optional.ofNullable(lastPart); + return Optional.ofNullable(latexFreeLastPart); } /** From 0d7f5cd740b4c72b8eb636d1885cdee7e0328aca Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 17:26:04 -0500 Subject: [PATCH 11/22] Fix tests --- .../groups/AutomaticPersonsGroupTest.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java index c1bd6642fe1..df336fd91b8 100644 --- a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java +++ b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java @@ -6,15 +6,17 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; +import org.jabref.model.strings.LatexToUnicodeAdapter; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; class AutomaticPersonsGroupTest { private static Set createPersonSubGroupFrom(String... lastNames) { - return Arrays.stream(lastNames).distinct() + return Arrays.stream(lastNames) + .distinct() + .map(LatexToUnicodeAdapter::format) .map(lastName -> new LastNameGroup(lastName, GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR, lastName)) .map(GroupTreeNode::new) @@ -32,20 +34,26 @@ void createSubgroupsFromCommaSeparatedLastNames() { @Test void createSubgroupsContainingCommaSeparatedLastNames() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan and Hopper, Grace"); - BibEntry turingEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan"); - BibEntry hopperEntry = new BibEntry().withField(StandardField.AUTHOR, "Hopper, Grace"); var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); var expectedSubgroups = createPersonSubGroupFrom("Turing", "Hopper"); assertEquals(expectedSubgroups, subgroups); } @Test - void createSubgroupContainingLatexAndUnicodeLastNames() { + void createSubgroupFromLatexAndCheckForUnicodeLastName() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt G{\\\"{o}}del"); BibEntry godelEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt Gödel"); var subgroup = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); - for (GroupTreeNode group : subgroup) { // There should only be one subgroup - assertTrue(group.matches(godelEntry)); - } + var expectedSubgroup = createPersonSubGroupFrom("Gödel"); + assertEquals(expectedSubgroup, subgroup); + } + + @Test + void createSubgroupFromUnicodeAndCheckForLatexLastName() { + BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt Gödel"); + BibEntry godelEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt G{\\\"{o}}del"); + var subgroup = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); + var expectedSubgroup = createPersonSubGroupFrom("G{\\\"{o}}del"); + assertEquals(expectedSubgroup, subgroup); } } From d13452160d1468db75ce0f677f17c5ce00dc7c10 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 17:27:30 -0500 Subject: [PATCH 12/22] Fix LastNameGroup for last name containing latex --- src/main/java/org/jabref/model/groups/LastNameGroup.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index 93880101555..08bcbe8cb47 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -10,13 +10,14 @@ import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.Field; +import org.jabref.model.strings.LatexToUnicodeAdapter; /** * Matches entries based on the last name of a list of authors in a specified field. */ public class LastNameGroup extends KeywordGroup { - public LastNameGroup(String name, GroupHierarchyType context, Field searchField, String searchExpression) { - super(name, context, searchField, searchExpression, true); + public LastNameGroup(String groupName, GroupHierarchyType context, Field searchField, String lastName) { + super(groupName, context, searchField, LatexToUnicodeAdapter.format(lastName), true); } static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { From 2ccb3ae23264eb8e20dbdae2b12bf75b9230ca29 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Wed, 30 Dec 2020 17:27:41 -0500 Subject: [PATCH 13/22] Readability and JavaDoc --- .../java/org/jabref/model/groups/LastNameGroup.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index 08bcbe8cb47..6996dba6572 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -13,7 +13,7 @@ import org.jabref.model.strings.LatexToUnicodeAdapter; /** - * Matches entries based on the last name of a list of authors in a specified field. + * Matches based on a latex free last name in a specified field. The field is parsed as an author list and the last names are resolved of latex. */ public class LastNameGroup extends KeywordGroup { public LastNameGroup(String groupName, GroupHierarchyType context, Field searchField, String lastName) { @@ -34,26 +34,26 @@ static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { @Override public boolean contains(BibEntry entry) { - return getAsLastNamesLatexFree(searchField, entry).stream().anyMatch(name -> name.equals(searchExpression)); + return getAsLastNamesLatexFree(getSearchField(), entry).stream().anyMatch(name -> name.equals(getSearchExpression())); } @Override public AbstractGroup deepCopy() { - return new LastNameGroup(getName(), context, searchField, searchExpression); + return new LastNameGroup(getName(), getHierarchicalContext(), getSearchField(), getSearchExpression()); } @Override public boolean equals(Object other) { if (super.equals(other)) { LastNameGroup otherGroup = (LastNameGroup) other; - return (this.searchField.equals(otherGroup.searchField) && - this.searchExpression.equals(otherGroup.searchExpression)); + return (getSearchField().equals(otherGroup.getSearchField()) && + getSearchExpression().equals(otherGroup.getSearchExpression())); } return false; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), searchField, searchExpression); + return Objects.hash(super.hashCode(), getSearchField(), getSearchExpression()); } } From fbe3325ea24f3a9b3ff63280bd1a2fc453530b0c Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Thu, 31 Dec 2020 10:01:36 -0500 Subject: [PATCH 14/22] Remove unused BibEntry --- .../java/org/jabref/model/groups/AutomaticPersonsGroupTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java index df336fd91b8..ecbeea9ceae 100644 --- a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java +++ b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java @@ -42,7 +42,6 @@ void createSubgroupsContainingCommaSeparatedLastNames() { @Test void createSubgroupFromLatexAndCheckForUnicodeLastName() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt G{\\\"{o}}del"); - BibEntry godelEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt Gödel"); var subgroup = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); var expectedSubgroup = createPersonSubGroupFrom("Gödel"); assertEquals(expectedSubgroup, subgroup); @@ -51,7 +50,6 @@ void createSubgroupFromLatexAndCheckForUnicodeLastName() { @Test void createSubgroupFromUnicodeAndCheckForLatexLastName() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt Gödel"); - BibEntry godelEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt G{\\\"{o}}del"); var subgroup = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); var expectedSubgroup = createPersonSubGroupFrom("G{\\\"{o}}del"); assertEquals(expectedSubgroup, subgroup); From 56cca39113f326c9a8693c971c579f0842005e0c Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Thu, 31 Dec 2020 10:02:29 -0500 Subject: [PATCH 15/22] Flatten Stream --- .../org/jabref/model/groups/LastNameGroup.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index 6996dba6572..2031b729a10 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -1,6 +1,6 @@ package org.jabref.model.groups; -import java.util.Collections; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -21,15 +21,13 @@ public LastNameGroup(String groupName, GroupHierarchyType context, Field searchF } static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { - return bibEntry.getField(field) + return bibEntry.getField(field).stream() .map(AuthorList::parse) .map(AuthorList::getAuthors) - .map(authors -> - authors.stream() - .map(Author::getLastLatexFree) - .flatMap(Optional::stream) - .collect(Collectors.toList())) - .orElse(Collections.emptyList()); + .flatMap(Collection::stream) + .map(Author::getLastLatexFree) + .flatMap(Optional::stream) + .collect(Collectors.toList()); } @Override From 6315c61b840027a0363948fa36fafc18197e1e82 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Sun, 3 Jan 2021 09:40:36 -0500 Subject: [PATCH 16/22] Fix test cases --- .../groups/AutomaticPersonsGroupTest.java | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java index ecbeea9ceae..f1e076261ab 100644 --- a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java +++ b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java @@ -1,26 +1,25 @@ package org.jabref.model.groups; import java.util.Arrays; -import java.util.Set; import java.util.stream.Collectors; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; -import org.jabref.model.strings.LatexToUnicodeAdapter; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; class AutomaticPersonsGroupTest { - private static Set createPersonSubGroupFrom(String... lastNames) { + private static GroupTreeNode[] createPersonSubGroupFrom(String... lastNames) { return Arrays.stream(lastNames) - .distinct() - .map(LatexToUnicodeAdapter::format) .map(lastName -> new LastNameGroup(lastName, GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR, lastName)) .map(GroupTreeNode::new) - .collect(Collectors.toSet()); + .collect(Collectors.toList()) + .toArray(GroupTreeNode[]::new); } @Test @@ -28,7 +27,7 @@ void createSubgroupsFromCommaSeparatedLastNames() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan and Hopper, Grace"); var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); var expectedSubgroups = createPersonSubGroupFrom("Turing", "Hopper"); - assertEquals(expectedSubgroups, subgroups); + assertThat(subgroups, containsInAnyOrder(expectedSubgroups)); } @Test @@ -36,22 +35,30 @@ void createSubgroupsContainingCommaSeparatedLastNames() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan and Hopper, Grace"); var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); var expectedSubgroups = createPersonSubGroupFrom("Turing", "Hopper"); - assertEquals(expectedSubgroups, subgroups); + assertThat(subgroups, containsInAnyOrder(expectedSubgroups)); } @Test - void createSubgroupFromLatexAndCheckForUnicodeLastName() { + void createSubgroupsContainingSpaceSeparatedNames() { + BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Alan Turing and Grace Hopper"); + var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); + var expectedSubgroups = createPersonSubGroupFrom("Turing", "Hopper"); + assertThat(subgroups, containsInAnyOrder(expectedSubgroups)); + } + + @Test + void createSubgroupFromLatex() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt G{\\\"{o}}del"); var subgroup = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); var expectedSubgroup = createPersonSubGroupFrom("Gödel"); - assertEquals(expectedSubgroup, subgroup); + assertThat(subgroup, contains(expectedSubgroup)); } @Test - void createSubgroupFromUnicodeAndCheckForLatexLastName() { + void createSubgroupFromUnicode() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Kurt Gödel"); var subgroup = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); - var expectedSubgroup = createPersonSubGroupFrom("G{\\\"{o}}del"); - assertEquals(expectedSubgroup, subgroup); + var expectedSubgroup = createPersonSubGroupFrom("Gödel"); + assertThat(subgroup, contains(expectedSubgroup)); } } From 30282373fe039365f809b8878cc7a52061de2641 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Sun, 3 Jan 2021 09:43:39 -0500 Subject: [PATCH 17/22] Remove duplicated test case --- .../jabref/model/groups/AutomaticPersonsGroupTest.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java index f1e076261ab..6bceb4625d7 100644 --- a/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java +++ b/src/test/java/org/jabref/model/groups/AutomaticPersonsGroupTest.java @@ -30,14 +30,6 @@ void createSubgroupsFromCommaSeparatedLastNames() { assertThat(subgroups, containsInAnyOrder(expectedSubgroups)); } - @Test - void createSubgroupsContainingCommaSeparatedLastNames() { - BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Turing, Alan and Hopper, Grace"); - var subgroups = new AutomaticPersonsGroup("", GroupHierarchyType.INDEPENDENT, StandardField.AUTHOR).createSubgroups(bibEntry); - var expectedSubgroups = createPersonSubGroupFrom("Turing", "Hopper"); - assertThat(subgroups, containsInAnyOrder(expectedSubgroups)); - } - @Test void createSubgroupsContainingSpaceSeparatedNames() { BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Alan Turing and Grace Hopper"); From 990fb34021cd0a03e7d3b190dd540e51cb59ac17 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Mon, 4 Jan 2021 08:25:52 -0500 Subject: [PATCH 18/22] Fix use of Objects.equals --- src/main/java/org/jabref/model/groups/LastNameGroup.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index 2031b729a10..48d8f00e9ff 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -44,8 +44,8 @@ public AbstractGroup deepCopy() { public boolean equals(Object other) { if (super.equals(other)) { LastNameGroup otherGroup = (LastNameGroup) other; - return (getSearchField().equals(otherGroup.getSearchField()) && - getSearchExpression().equals(otherGroup.getSearchExpression())); + return (Objects.equals(getSearchField(), otherGroup.getSearchField()) && + Objects.equals(getSearchExpression(), otherGroup.getSearchExpression())); } return false; } From c43da991c0c094d183064c0b6bf52b9dd4d849af Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Mon, 4 Jan 2021 15:52:08 -0500 Subject: [PATCH 19/22] Fix equality check --- .../org/jabref/model/groups/KeywordGroup.java | 18 ++++++++++++++++++ .../org/jabref/model/groups/LastNameGroup.java | 15 --------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/KeywordGroup.java b/src/main/java/org/jabref/model/groups/KeywordGroup.java index e0018f4f32e..875bcb39207 100644 --- a/src/main/java/org/jabref/model/groups/KeywordGroup.java +++ b/src/main/java/org/jabref/model/groups/KeywordGroup.java @@ -1,5 +1,7 @@ package org.jabref.model.groups; +import java.util.Objects; + import org.jabref.model.entry.field.Field; /** @@ -33,4 +35,20 @@ public Field getSearchField() { public boolean isDynamic() { return true; } + + @Override + public boolean equals(Object other) { + if (super.equals(other)) { + KeywordGroup otherGroup = (KeywordGroup) other; + return (Objects.equals(isCaseSensitive(), otherGroup.isCaseSensitive()) && + Objects.equals(getSearchField(), otherGroup.getSearchField()) && + Objects.equals(getSearchExpression(), otherGroup.getSearchExpression())); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), isCaseSensitive(), getSearchField(), getSearchExpression()); + } } diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index 48d8f00e9ff..bbb3106a79b 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -39,19 +39,4 @@ public boolean contains(BibEntry entry) { public AbstractGroup deepCopy() { return new LastNameGroup(getName(), getHierarchicalContext(), getSearchField(), getSearchExpression()); } - - @Override - public boolean equals(Object other) { - if (super.equals(other)) { - LastNameGroup otherGroup = (LastNameGroup) other; - return (Objects.equals(getSearchField(), otherGroup.getSearchField()) && - Objects.equals(getSearchExpression(), otherGroup.getSearchExpression())); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), getSearchField(), getSearchExpression()); - } } From 450ba106cb833cf4e8e044914102a4cb79e0dfa0 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Mon, 4 Jan 2021 16:04:11 -0500 Subject: [PATCH 20/22] Optimized imports --- src/main/java/org/jabref/model/groups/LastNameGroup.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index bbb3106a79b..f2d771bbb8f 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; From edd24bdb004f3447fba938eab3318dbb6c0b8e83 Mon Sep 17 00:00:00 2001 From: Jonatan Asketorp <2598631+k3KAW8Pnf7mkmdSMPHz27@users.noreply.github.com> Date: Tue, 12 Jan 2021 17:28:04 -0500 Subject: [PATCH 21/22] Add intellij generated equal and hashCode --- .../org/jabref/model/groups/KeywordGroup.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jabref/model/groups/KeywordGroup.java b/src/main/java/org/jabref/model/groups/KeywordGroup.java index 875bcb39207..22511e2e049 100644 --- a/src/main/java/org/jabref/model/groups/KeywordGroup.java +++ b/src/main/java/org/jabref/model/groups/KeywordGroup.java @@ -37,18 +37,22 @@ public boolean isDynamic() { } @Override - public boolean equals(Object other) { - if (super.equals(other)) { - KeywordGroup otherGroup = (KeywordGroup) other; - return (Objects.equals(isCaseSensitive(), otherGroup.isCaseSensitive()) && - Objects.equals(getSearchField(), otherGroup.getSearchField()) && - Objects.equals(getSearchExpression(), otherGroup.getSearchExpression())); + public boolean equals(Object o) { + if (this == o) { + return true; } - return false; + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + KeywordGroup that = (KeywordGroup) o; + return isCaseSensitive() == that.isCaseSensitive() && Objects.equals(getSearchField(), that.getSearchField()) && Objects.equals(getSearchExpression(), that.getSearchExpression()); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), isCaseSensitive(), getSearchField(), getSearchExpression()); + return Objects.hash(super.hashCode(), getSearchField(), getSearchExpression(), isCaseSensitive()); } } From 805187c54b00e36ca5bc6b7683676b063f9b2f3e Mon Sep 17 00:00:00 2001 From: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> Date: Mon, 18 Jan 2021 20:47:28 +0100 Subject: [PATCH 22/22] Fixed merge error --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f068388a18..59ab7e13f0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve ### Changed - The content of the field `timestamp` is migrated to `creationdate`. In case one configured "udpate timestampe", it is migrated to `modificationdate`. [koppor#130](https://github.com/koppor/jabref/issues/130) +- We fixed an issue where groups generated from authors' last names did not include all entries of the authors' [#5833](https://github.com/JabRef/jabref/issues/5833) - The export to MS Office XML now uses the month name for the field `MonthAcessed` instead of the two digit number [#7354](https://github.com/JabRef/jabref/issues/7354) ### Fixed @@ -107,7 +108,6 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where the password for a shared SQL database was not remembered [#6869](https://github.com/JabRef/jabref/issues/6869) - We fixed an issue where newly added entires were not synced to a shared SQL database [#7176](https://github.com/JabRef/jabref/issues/7176) - We fixed an issue where the PDF-Content importer threw an exception when no DOI number is present at the first page of the PDF document [#7203](https://github.com/JabRef/jabref/issues/7203) -- We fixed an issue where groups generated from authors' last names did not include all entries of the authors' [#5833](https://github.com/JabRef/jabref/issues/5833) - We fixed an issue where groups created from aux files did not update on file changes [#6394](https://github.com/JabRef/jabref/issues/6394) - We fixed an issue where authors that only have last names were incorrectly identified as institutes when generating citation keys [#7199](https://github.com/JabRef/jabref/issues/7199) - We fixed an issue where institutes were incorrectly identified as universities when generating citation keys [#6942](https://github.com/JabRef/jabref/issues/6942)