Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix authorsAlpha #11614

Merged
merged 21 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import org.jabref.logic.cleanup.Formatter;
import org.jabref.logic.formatter.Formatters;
import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter;
import org.jabref.logic.formatter.casechanger.Word;
import org.jabref.logic.layout.format.RemoveLatexCommandsFormatter;
import org.jabref.model.database.BibDatabase;
Expand Down Expand Up @@ -84,6 +85,8 @@ public class BracketedPattern {

private static final Pattern WHITESPACE = Pattern.compile("\\p{javaWhitespace}");

private static final RemoveEnclosingBracesFormatter ENCLOSING_BRACES_FORMATTER = new RemoveEnclosingBracesFormatter();

private enum Institution {
SCHOOL,
DEPARTMENT,
Expand Down Expand Up @@ -846,51 +849,44 @@ static String allAuthors(AuthorList authorList) {
*/
public static String authorsAlpha(AuthorList authorList) {
StringBuilder alphaStyle = new StringBuilder();
int maxAuthors;
final boolean maxAuthorsExceeded;
if (authorList.getNumberOfAuthors() <= MAX_ALPHA_AUTHORS) {
maxAuthors = authorList.getNumberOfAuthors();
maxAuthorsExceeded = false;
} else {
maxAuthors = MAX_ALPHA_AUTHORS - 1;
maxAuthorsExceeded = true;
}

if (authorList.getNumberOfAuthors() == 1) {
String[] firstAuthor = authorList.getAuthor(0).getNamePrefixAndFamilyName()
.replaceAll("\\s+", " ").trim().split(" ");
// take first letter of any "prefixes" (e.g. van der Aalst -> vd)
for (int j = 0; j < (firstAuthor.length - 1); j++) {
alphaStyle.append(firstAuthor[j], 0, 1);
int numberOfAuthors = authorList.getNumberOfAuthors();
boolean andOthersPresent = numberOfAuthors > 1 &&
authorList.getAuthor(numberOfAuthors - 1).equals(Author.OTHERS);

if (numberOfAuthors == 1 || andOthersPresent) {
// Single author or "and others" case
String lastName = authorList.getAuthor(0).getFamilyName().orElse("");
String formattedName = ENCLOSING_BRACES_FORMATTER.format(lastName);
if (!formattedName.equals(lastName)) {
// Inequality => braces were removed, indicating an organization
alphaStyle.append(getOrganizationInitials(formattedName));
} else {
alphaStyle.append(lastName, 0, Math.min(2, lastName.length()));
}
// append last part of last name completely
alphaStyle.append(firstAuthor[firstAuthor.length - 1], 0,
Math.min(3, firstAuthor[firstAuthor.length - 1].length()));
} else {
boolean andOthersPresent = authorList.getAuthor(maxAuthors - 1).equals(Author.OTHERS);
if (andOthersPresent) {
maxAuthors--;
}
List<String> vonAndLastNames = authorList.getAuthors().stream()
.limit(maxAuthors)
.map(Author::getNamePrefixAndFamilyName)
.toList();
for (String vonAndLast : vonAndLastNames) {
// replace all whitespaces by " "
// split the lastname at " "
String[] nameParts = vonAndLast.replaceAll("\\s+", " ").trim().split(" ");
for (String part : nameParts) {
// use first character of each part of lastname
alphaStyle.append(part, 0, 1);
int maxAuthors = Math.min(numberOfAuthors, MAX_ALPHA_AUTHORS);
for (int i = 0; i < maxAuthors; i++) {
String lastName = authorList.getAuthor(i).getFamilyName().orElse("");
alphaStyle.append(lastName, 0, 1);
if (alphaStyle.length() >= 4) {
// Stop after 4 authors
break;
}
}
if (andOthersPresent || maxAuthorsExceeded) {
alphaStyle.append("+");
}
}
return alphaStyle.toString();
}

private static String getOrganizationInitials(String orgName) {
StringBuilder initials = new StringBuilder();
for (String part : orgName.split("\\s+")) {
if (!part.isEmpty() && Character.isUpperCase(part.charAt(0))) {
initials.append(part.charAt(0));
}
}
return initials.toString();
}

/**
* Creates a string with all last names separated by a `delimiter`. If the number of authors are larger than
* `maxAuthors`, replace all excess authors with `suffix`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class AbstractCitationKeyPatternsTest {

@Test
void AbstractCitationKeyPatternParse() throws Exception {
void AbstractCitationKeyPatternParse() {
AbstractCitationKeyPatterns pattern = mock(AbstractCitationKeyPatterns.class, Mockito.CALLS_REAL_METHODS);

pattern.setDefaultValue("[field1]spacer1[field2]spacer2[field3]");
Expand All @@ -21,7 +21,7 @@ void AbstractCitationKeyPatternParse() throws Exception {
}

@Test
void AbstractCitationKeyPatternParseEmptySpacer() throws Exception {
void AbstractCitationKeyPatternParseEmptySpacer() {
AbstractCitationKeyPatterns pattern = mock(AbstractCitationKeyPatterns.class, Mockito.CALLS_REAL_METHODS);

pattern.setDefaultValue("[field1][field2]spacer2[field3]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,27 @@ void allAuthors(String expected, AuthorList list) {

static Stream<Arguments> authorsAlpha() {
return Stream.of(
Arguments.of("A+", "Alexander Artemenko and others"),
Arguments.of("A+", "Aachen and others"),
Arguments.of("AB+", "Aachen and Berlin and others"),
Arguments.of("ABC+", "Aachen and Berlin and Chemnitz and others"),
Arguments.of("Ar", "Alexander Artemenko and others"),
Arguments.of("Aa", "Aachen and others"),
Arguments.of("Aa", "Aachen and Berlin and others"),
Arguments.of("Aa", "Aachen and Berlin and Chemnitz and others"),
Arguments.of("AB", "Aachen and Berlin"),
Arguments.of("ABC", "Aachen and Berlin and Chemnitz"),
Arguments.of("ABCD", "Aachen and Berlin and Chemnitz and Düsseldorf"),
Arguments.of("ABC+", "Aachen and Berlin and Chemnitz and Düsseldorf and others"),
Arguments.of("ABC+", "Aachen and Berlin and Chemnitz and Düsseldorf and Essen"),
Arguments.of("ABC+", "Aachen and Berlin and Chemnitz and Düsseldorf and Essen and others"));
Arguments.of("Aa", "Aachen and Berlin and Chemnitz and Düsseldorf and others"),
Arguments.of("ABCD", "Aachen and Berlin and Chemnitz and Düsseldorf and Essen"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does exceed 4 authors, why is this not Aa?

Copy link
Member Author

@subhramit subhramit Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per our discussion, we would do that if there is "and others". (Refer Case "1"). Furthermore, this has been established ever since we started talking about DIN. I can do only the first author's two letters of last name in case of exceeding authors but note that everywhere I have looked, in case of exceeding authors (and no "and others"), the abbreviation of first four authors are taken.
You can try citing 10.3945/ajcn.111.023457 using citationmachine as well: https://www.citationmachine.net/bibliographies/a446c586-d900-4583-a249-72d7b9bc0b52
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the LNI template: #11614 (comment)

We need to setup a minimal latex+bibtex document with authorsalpha with the test cases and see what that returns.

Arguments.of("Aa", "Aachen and Berlin and Chemnitz and Düsseldorf and Essen and others"),
Arguments.of("AB", "Abel, K.; Bibel, U."),
Arguments.of("ABC", "Abraham, N.; Bibel, U.; Corleone, P."),
Arguments.of("Az", "Azubi, L. et.al."),
Arguments.of("Ez", "Ezgarani, O."),
Arguments.of("GI", "GI, Gesellschaft für Informatik e.V."),
Arguments.of("Gl", "Glück, H. I."),
Arguments.of("Go", "von Goethe"),
Arguments.of("Aa", "van der Aalst"),
Arguments.of("AW", "van der Aalst and Weske"),
Arguments.of("GI", "{Gesellschaft für Informatik e.V.}"),
Arguments.of("AF", "{Apache Foundation}"));
}

@ParameterizedTest
Expand Down Expand Up @@ -305,7 +318,7 @@ void authShort(String expected, AuthorList list) {
"'New', '[auth3]', 'Isaac Newton'",
"'New', '[auth3_1]', 'Isaac Newton'",
"'Newton', '[authshort]', 'Isaac Newton'",
"'New', '[authorsAlpha]', 'Isaac Newton'",
"'Ne', '[authorsAlpha]', 'Isaac Newton'",
"'Newton', '[authorLast]', 'Isaac Newton'",
"'I', '[authorLastForeIni]', 'Isaac Newton'",

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,14 +505,14 @@ void allAuthors() {

static Stream<Arguments> authorsAlpha() {
return Stream.of(
Arguments.of("New", AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHORSALPHA),
Arguments.of("Ne", AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHORSALPHA),
Arguments.of("NM", AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHORSALPHA),
Arguments.of("NME", AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, AUTHORSALPHA),
Arguments.of("NMEB", AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, AUTHORSALPHA),
Arguments.of("NME+", AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, AUTHORSALPHA),
Arguments.of("vdAal", AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, AUTHORSALPHA),
Arguments.of("vdAvL", AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, AUTHORSALPHA),
Arguments.of("NM+", AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_AND_OTHERS_COUNT_3, AUTHORSALPHA)
Arguments.of("NMEB", AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, AUTHORSALPHA),
Arguments.of("Aa", AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, AUTHORSALPHA),
Arguments.of("AL", AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, AUTHORSALPHA),
Arguments.of("Ne", AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_AND_OTHERS_COUNT_3, AUTHORSALPHA)
);
}

Expand Down
38 changes: 19 additions & 19 deletions src/test/java/org/jabref/model/entry/BibEntryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ void setNullFieldThrowsNPE() {
}

@Test
void getFieldIsCaseInsensitive() throws Exception {
void getFieldIsCaseInsensitive() {
entry.setField(new UnknownField("TeSt"), "value");
assertEquals(Optional.of("value"), entry.getField(new UnknownField("tEsT")));
}

@Test
void getFieldWorksWithBibFieldAsWell() throws Exception {
void getFieldWorksWithBibFieldAsWell() {
entry.setField(StandardField.AUTHOR, "value");
assertEquals(Optional.of("value"), entry.getField(new BibField(StandardField.AUTHOR, FieldPriority.IMPORTANT).field()));
}
Expand All @@ -76,80 +76,80 @@ void newBibEntryIsUnchanged() {
}

@Test
void setFieldLeadsToAChangedEntry() throws Exception {
void setFieldLeadsToAChangedEntry() {
entry.setField(StandardField.AUTHOR, "value");
assertTrue(entry.hasChanged());
}

@Test
void setFieldWorksWithBibFieldAsWell() throws Exception {
void setFieldWorksWithBibFieldAsWell() {
entry.setField(new BibField(StandardField.AUTHOR, FieldPriority.IMPORTANT).field(), "value");
assertEquals(Optional.of("value"), entry.getField(StandardField.AUTHOR));
}

@Test
void clonedBibEntryHasUniqueID() throws Exception {
void clonedBibEntryHasUniqueID() {
BibEntry entryClone = (BibEntry) entry.clone();
assertNotEquals(entry.getId(), entryClone.getId());
}

@Test
void clonedBibEntryWithMiscTypeHasOriginalChangedFlag() throws Exception {
void clonedBibEntryWithMiscTypeHasOriginalChangedFlag() {
BibEntry entryClone = (BibEntry) entry.clone();
assertFalse(entryClone.hasChanged());
}

@Test
void clonedBibEntryWithBookTypeAndOneFieldHasOriginalChangedFlag() throws Exception {
void clonedBibEntryWithBookTypeAndOneFieldHasOriginalChangedFlag() {
entry = new BibEntry(StandardEntryType.Book).withField(StandardField.AUTHOR, "value");
BibEntry entryClone = (BibEntry) entry.clone();
assertFalse(entryClone.hasChanged());
}

@Test
void setAndGetAreConsistentForMonth() throws Exception {
void setAndGetAreConsistentForMonth() {
entry.setField(StandardField.MONTH, "may");
assertEquals(Optional.of("may"), entry.getField(StandardField.MONTH));
}

@Test
void setAndGetAreConsistentForCapitalizedMonth() throws Exception {
void setAndGetAreConsistentForCapitalizedMonth() {
entry.setField(StandardField.MONTH, "May");
assertEquals(Optional.of("May"), entry.getField(StandardField.MONTH));
}

@Test
void setAndGetAreConsistentForMonthString() throws Exception {
void setAndGetAreConsistentForMonthString() {
entry.setField(StandardField.MONTH, "#may#");
assertEquals(Optional.of("#may#"), entry.getField(StandardField.MONTH));
}

@Test
void monthCorrectlyReturnedForMonth() throws Exception {
void monthCorrectlyReturnedForMonth() {
entry.setField(StandardField.MONTH, "may");
assertEquals(Optional.of(Month.MAY), entry.getMonth());
}

@Test
void monthCorrectlyReturnedForCapitalizedMonth() throws Exception {
void monthCorrectlyReturnedForCapitalizedMonth() {
entry.setField(StandardField.MONTH, "May");
assertEquals(Optional.of(Month.MAY), entry.getMonth());
}

@Test
void monthCorrectlyReturnedForMonthString() throws Exception {
void monthCorrectlyReturnedForMonthString() {
entry.setField(StandardField.MONTH, "#may#");
assertEquals(Optional.of(Month.MAY), entry.getMonth());
}

@Test
void monthCorrectlyReturnedForMonthMay() throws Exception {
void monthCorrectlyReturnedForMonthMay() {
entry.setMonth(Month.MAY);
assertEquals(Optional.of(Month.MAY), entry.getMonth());
}

@Test
void monthFieldCorrectlyReturnedForMonthMay() throws Exception {
void monthFieldCorrectlyReturnedForMonthMay() {
entry.setMonth(Month.MAY);
assertEquals(Optional.of("#may#"), entry.getField(StandardField.MONTH));
}
Expand Down Expand Up @@ -362,23 +362,23 @@ void isEmptyCiteKey() {
}

@Test
void identicObjectsareEqual() throws Exception {
void identicObjectsareEqual() {
BibEntry otherEntry = entry;
assertEquals(entry, otherEntry);
}

@Test
void compareToNullObjectIsFalse() throws Exception {
void compareToNullObjectIsFalse() {
assertNotEquals(null, entry);
}

@Test
void compareToDifferentClassIsFalse() throws Exception {
void compareToDifferentClassIsFalse() {
assertNotEquals(entry, new Object());
}

@Test
void compareIsTrueWhenIdAndFieldsAreEqual() throws Exception {
void compareIsTrueWhenIdAndFieldsAreEqual() {
entry.setId("1");
entry.setField(new UnknownField("key"), "value");
BibEntry otherEntry = new BibEntry();
Expand Down
Loading