Skip to content

Commit 08eccb6

Browse files
authoredMay 28, 2020
Fixes generated bibtex key and display of institute authors (#6479)
* Fix Pattern.compile for frequently used regexes * Fix one additional Pattern.compile * Fix style and unnecessary escape sequences * Fix invalid index in call to substring The original condition is evaluated to false. The substring is shorter than "uni". * Refactor name and javadoc of a regex * Fix use of compiled regex for matching department * Fix check for uppercase letter Perhaps the assumption should be that the letters are ASCII. If all letters are ASCII checking 'A' <= k.charAt(0) <= 'Z' might make more sense. I am not convinced about doing this with a regex. * Fix usage of uncompiled regex * Fix readability? * Add test cases Both test cases involves an author name containing department or school without university or institute of technology. * Fix `null` appearing as part of author name Corporate authors without university/institute of technology * Refactor name of capital regex pattern * Add debug output for reordering of names in fields * Add helper methods * Fix missing negation in "uni" matching * Fix test cases for corporate authors * Fix to keep all uppercase letters in abbreviation * Fix commented out code * Fix key for institution's name containing keyword If the name of an institution can't be split, assume that "School"/"Department" is part of the name. * Fix test case for short institution name * Refactor check for institution types * Refactor comments and names improving readability? * Refactor to improve readability and closure * Fix JavaDoc Minor typos and the "rest" part is now created differently. * Fix JavaDoc typos * Fix preliminary order for authors -> latexfree * Drop logger * Add convenience methods for cached latexfree names * Add name format method for names containing latex * Add call to formatNameLatexFree * Fix unclear statement in JavaDoc * Fix to only keep the first character of each word There are some examples that will turn out wrong if only capital letters are kept, e.g., "iOS Developer University Program". The original problem with "The School of Life" becoming too short is avoided by only removing school/department for names containing two or more ',' separated strings. This will still produce an unexpected result if the address of the institution is part of the authors field * Add latexfree Natbib test cases * Fix typo in latex-free test cases * Add Natbib test with escaped brackets * Add Natbib institution test with escaped brackets * Add test for latex-free comma separated lastnames * Add test for latex-free comma separated first name First name first and abbreviated first name first * Add test for latex-free comma separated last name Last name first and abbreviated first names * Fix adherence to JavaDoc and readability(?) * Fix readability(?) * Fix CheckStyle issues The deprecated static methods BibtexKeyGenerator.generateKey are moved to the test file as a similar convenience method is required for the test cases. Suppress warning has been added for some methods. * Fix CHANGELOG.md * Fix mistake in BibtexKeyGeneratorTest generateKey was not copy-pasted properly. * Add test for oxford comma * Fix miss-capitalization of enum * Fix fields not displayed latex-free * Fix in-line methods in MainTableNameFormatter * Fix in-line of generateKey() method * Fix separating tests into parsing/representation * Fix cache check and simplify expressions * Drop inlined methods * Fix most abbreviated abbreviations * Drop old formatName method * Refactor formatNameLatexFree The author list parsing is moved outside of the if/else statements * Refactor new parse tests * Add more parse tests * Drop all test cases containing escaped brackets * Refactor parse with latex tests Move them close to other parse tests * Fix my own spelling mistakes * Refactor abbreviation name
1 parent c119ca2 commit 08eccb6

File tree

7 files changed

+894
-287
lines changed

7 files changed

+894
-287
lines changed
 

‎CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve
7575
- We fixed an issue where brackets in regular expressions were not working. [6469](https://github.com/JabRef/jabref/pull/6469)
7676
- We fixed an issue where LaTeX citations for specific commands (\autocites) of biblatex-mla were not recognized. [#6476](https://github.com/JabRef/jabref/issues/6476)
7777
- We fixed an issue where drag and drop was not working on empty database. [#6487](https://github.com/JabRef/jabref/issues/6487)
78+
- We fixed an issue where "null" appeared in generated BibTeX keys. [#6459](https://github.com/JabRef/jabref/issues/6459)
79+
- We fixed an issue where the authors' names were incorrectly displayed in the authors' column when they were bracketed. [#6465](https://github.com/JabRef/jabref/issues/6465) [#6459](https://github.com/JabRef/jabref/issues/6459)
7880

7981
### Removed
8082

‎src/main/java/org/jabref/gui/maintable/BibEntryTableViewModel.java

+18-19
Original file line numberDiff line numberDiff line change
@@ -115,28 +115,27 @@ public ObservableValue<String> getFields(OrFields fields) {
115115
ObservableValue<String> value = fieldValues.get(fields);
116116
if (value != null) {
117117
return value;
118-
} else {
119-
value = Bindings.createStringBinding(() -> {
120-
boolean isName = false;
118+
}
121119

122-
Optional<String> content = Optional.empty();
123-
for (Field field : fields) {
124-
content = entry.getResolvedFieldOrAliasLatexFree(field, database);
125-
if (content.isPresent()) {
126-
isName = field.getProperties().contains(FieldProperty.PERSON_NAMES);
127-
break;
128-
}
129-
}
120+
value = Bindings.createStringBinding(() -> {
121+
for (Field field : fields) {
122+
if (field.getProperties().contains(FieldProperty.PERSON_NAMES)) {
123+
Optional<String> name = entry.getResolvedFieldOrAlias(field, database);
130124

131-
String result = content.orElse(null);
132-
if (isName) {
133-
return nameFormatter.formatName(result);
125+
if (name.isPresent()) {
126+
return nameFormatter.formatNameLatexFree(name.get());
127+
}
134128
} else {
135-
return result;
129+
Optional<String> content = entry.getResolvedFieldOrAliasLatexFree(field, database);
130+
131+
if (content.isPresent()) {
132+
return content.get();
133+
}
136134
}
137-
}, entry.getObservables());
138-
fieldValues.put(fields, value);
139-
return value;
140-
}
135+
}
136+
return "";
137+
}, entry.getObservables());
138+
fieldValues.put(fields, value);
139+
return value;
141140
}
142141
}

‎src/main/java/org/jabref/gui/maintable/MainTableNameFormatter.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,28 @@ public class MainTableNameFormatter {
2020
}
2121

2222
/**
23-
* Format a name field for the table, according to user preferences.
23+
* Format a name field for the table, according to user preferences and with latex expressions translated if
24+
* possible.
2425
*
2526
* @param nameToFormat The contents of the name field.
2627
* @return The formatted name field.
2728
*/
28-
public String formatName(final String nameToFormat) {
29+
public String formatNameLatexFree(final String nameToFormat) {
2930
if (nameToFormat == null) {
3031
return null;
3132
}
33+
AuthorList authors = AuthorList.parse(nameToFormat);
3234

3335
if (namesAsIs) {
3436
return nameToFormat;
3537
} else if (namesNatbib) {
36-
return AuthorList.fixAuthorNatbib(nameToFormat);
38+
return authors.getAsNatbibLatexFree();
3739
} else if (namesLastOnly) {
38-
return AuthorList.fixAuthorLastNameOnlyCommas(nameToFormat, false);
40+
return authors.getAsLastNamesLatexFree(false);
3941
} else if (namesFf) {
40-
return AuthorList.fixAuthorFirstNameFirstCommas(nameToFormat, abbrAuthorNames, false);
42+
return authors.getAsFirstLastNamesLatexFree(abbrAuthorNames, false);
4143
} else {
42-
return AuthorList.fixAuthorLastNameFirstCommas(nameToFormat, abbrAuthorNames, false);
44+
return authors.getAsLastFirstNamesLatexFree(abbrAuthorNames, false);
4345
}
4446
}
4547
}

‎src/main/java/org/jabref/logic/bibtexkeypattern/BracketedPattern.java

+132-97
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import java.util.ArrayList;
44
import java.util.Arrays;
55
import java.util.Collections;
6+
import java.util.EnumSet;
67
import java.util.List;
78
import java.util.Locale;
89
import java.util.Objects;
910
import java.util.Optional;
1011
import java.util.Scanner;
1112
import java.util.StringJoiner;
1213
import java.util.StringTokenizer;
14+
import java.util.function.Predicate;
1315
import java.util.regex.Matcher;
1416
import java.util.regex.Pattern;
1517

@@ -41,9 +43,54 @@
4143
public class BracketedPattern {
4244
private static final Logger LOGGER = LoggerFactory.getLogger(BracketedPattern.class);
4345

44-
private static final String STARTING_CAPITAL_PATTERN = "[^A-Z]";
4546
private static final int CHARS_OF_FIRST = 5;
46-
private static final Pattern REGEX_PATTERN = Pattern.compile(".*\\(\\{([A-Z]+)\\}\\).*");
47+
48+
/** Matches everything that is not an uppercase ASCII letter */
49+
private static final Pattern NOT_CAPITAL_FIRST_CHARACTER = Pattern.compile("[^A-Z]");
50+
/** Matches with "({[A-Z]}+)", which should be used to abbreviate the name of an institution */
51+
private static final Pattern ABBREVIATIONS = Pattern.compile(".*\\(\\{[A-Z]+}\\).*");
52+
/** Matches with "dep"/"dip", case insensitive */
53+
private static final Pattern DEPARTMENTS = Pattern.compile("^d[ei]p.*", Pattern.CASE_INSENSITIVE);
54+
private enum Institution {
55+
SCHOOL,
56+
DEPARTMENT,
57+
UNIVERSITY,
58+
TECHNOLOGY;
59+
60+
/** Matches "uni" at the start of a string or after a space, case insensitive */
61+
private static final Pattern UNIVERSITIES = Pattern.compile("^uni.*", Pattern.CASE_INSENSITIVE);
62+
/** Matches with "tech", case insensitive */
63+
private static final Pattern TECHNOLOGICAL_INSTITUTES = Pattern.compile("^tech.*", Pattern.CASE_INSENSITIVE);
64+
/** Matches with "dep"/"dip"/"lab", case insensitive */
65+
private static final Pattern DEPARTMENTS_OR_LABS = Pattern.compile("^(d[ei]p|lab).*", Pattern.CASE_INSENSITIVE);
66+
67+
/**
68+
* Find which types of institutions have words in common with the given name parts.
69+
* @param nameParts a list of words that constitute parts of an institution's name.
70+
* @return set containing all types that matches
71+
*/
72+
public static EnumSet<Institution> findTypes(List<String> nameParts) {
73+
EnumSet<Institution> parts = EnumSet.noneOf(Institution.class);
74+
// Deciding about a part type…
75+
for (String namePart : nameParts) {
76+
if (UNIVERSITIES.matcher(namePart).matches()) {
77+
parts.add(Institution.UNIVERSITY);
78+
} else if (TECHNOLOGICAL_INSTITUTES.matcher(namePart).matches()) {
79+
parts.add(Institution.TECHNOLOGY);
80+
} else if (StandardField.SCHOOL.getName().equalsIgnoreCase(namePart)) {
81+
parts.add(Institution.SCHOOL);
82+
} else if (DEPARTMENTS_OR_LABS.matcher(namePart).matches()) {
83+
parts.add(Institution.DEPARTMENT);
84+
}
85+
}
86+
87+
if (parts.contains(Institution.TECHNOLOGY)) {
88+
parts.remove(Institution.UNIVERSITY); // technology institute isn't university :-)
89+
}
90+
91+
return parts;
92+
}
93+
}
4794

4895
private final String pattern;
4996

@@ -1200,8 +1247,8 @@ private static boolean isInstitution(String author) {
12001247
*
12011248
* <p>
12021249
* An institution name should be inside <code>{}</code> brackets. If the
1203-
* institution name also includes its abbreviation this abbreviation should
1204-
* be also in <code>{}</code> brackets. For the previous example the value
1250+
* institution name includes its abbreviation this abbreviation should
1251+
* be in <code>{}</code> brackets. For the previous example the value
12051252
* should look like:
12061253
* <code>{The Attributed Graph Grammar System ({AGG})}</code>.
12071254
* </p>
@@ -1213,33 +1260,34 @@ private static boolean isInstitution(String author) {
12131260
*
12141261
* <p>
12151262
* If an institution does not include its abbreviation the key should be
1216-
* generated form its name in the following way:
1263+
* generated from its name in the following way:
12171264
* </p>
12181265
*
12191266
* <p>
12201267
* The institution value can contain: institution name, part of the
1221-
* institution, address, etc. Those information should be separated by
1222-
* comma. Name of the institution and possible part of the institution
1223-
* should be on the beginning, while address and secondary information
1224-
* should be on the end.
1268+
* institution, address, etc. These values should be comma separated.
1269+
* Institution name and possible part of the institution
1270+
* should be in the beginning, while address and secondary information
1271+
* should be in the end.
12251272
* </p>
12261273
*
12271274
* Each part is examined separately:
12281275
* <ol>
12291276
* <li>We remove all tokens of a part which are one of the defined ignore
12301277
* words (the, press), which end with a dot (ltd., co., ...) and which first
12311278
* character is lowercase (of, on, di, ...).</li>
1232-
* <li>We detect a type of the part: university, technology institute,
1279+
* <li>We detect the types of the part: university, technology institute,
12331280
* department, school, rest
12341281
* <ul>
12351282
* <li>University: <code>"Uni[NameOfTheUniversity]"</code></li>
1236-
* <li>Department: will be an abbreviation of all words beginning with the
1237-
* uppercase letter except of words: <code>d[ei]p.*</code>, school,
1238-
* faculty</li>
1283+
* <li>Department: If the institution value contains more than one comma
1284+
* separated part, the department will be an abbreviation of all words
1285+
* beginning with the uppercase letter except of words:
1286+
* <code>d[ei]p.*</code>, school, faculty</li>
12391287
* <li>School: same as department</li>
12401288
* <li>Rest: If there are less than 3 tokens in such part than the result
1241-
* will be by concatenating those tokens, otherwise the result will be build
1242-
* from the first letters of words starting with and uppercase letter.</li>
1289+
* is a concatenation of those tokens. Otherwise, the result will be built
1290+
* from the first letter in each token.</li>
12431291
* </ul>
12441292
* </ol>
12451293
* <p>
@@ -1262,134 +1310,121 @@ private static boolean isInstitution(String author) {
12621310
* </ul>
12631311
*/
12641312
private static String generateInstitutionKey(String content) {
1265-
if (content.isEmpty()) {
1266-
return content;
1313+
if (content == null) {
1314+
return null;
1315+
}
1316+
if (content.isBlank()) {
1317+
return "";
12671318
}
12681319

12691320
String result = content;
12701321
result = unifyDiacritics(result);
1271-
result = result.replaceAll("^\\{", "").replaceAll("\\}$", "");
1272-
Matcher matcher = REGEX_PATTERN.matcher(result);
1322+
result = result.replaceAll("^\\{", "").replaceAll("}$", "");
1323+
Matcher matcher = ABBREVIATIONS.matcher(result);
12731324
if (matcher.matches()) {
12741325
return matcher.group(1);
12751326
}
12761327

12771328
result = removeDiacritics(result);
1278-
String[] parts = result.split(",");
1329+
String[] institutionNameTokens = result.split(",");
12791330

12801331
// Key parts
12811332
String university = null;
12821333
String department = null;
12831334
String school = null;
12841335
String rest = null;
12851336

1286-
List<String> ignore = Arrays.asList("press", "the");
1287-
for (int index = 0; index < parts.length; index++) {
1288-
List<String> part = new ArrayList<>();
1289-
1290-
// Cleanup: remove unnecessary words.
1291-
for (String k : parts[index].replaceAll("\\{[A-Z]+\\}", "").split("[ \\-_]")) {
1292-
if ((!(k.isEmpty()) // remove empty
1293-
&& !ignore.contains(k.toLowerCase(Locale.ENGLISH)) // remove ignored words
1294-
&& (k.charAt(k.length() - 1) != '.')
1295-
&& (String.valueOf(k.charAt(0))).matches("[A-Z]"))
1296-
|| ((k.length() >= 3) && "uni".equalsIgnoreCase(k.substring(0, 2)))) {
1297-
part.add(k);
1298-
}
1299-
}
1337+
for (int index = 0; index < institutionNameTokens.length; index++) {
1338+
List<String> tokenParts = getValidInstitutionNameParts(institutionNameTokens[index]);
1339+
EnumSet<Institution> tokenTypes = Institution.findTypes(tokenParts);
13001340

1301-
boolean isUniversity = false; // university
1302-
boolean isTechnology = false; // technology institute
1303-
boolean isDepartment = false; // departments
1304-
boolean isSchool = false; // schools
1305-
1306-
// Deciding about a part type...
1307-
for (String k : part) {
1308-
if (k.matches("^[Uu][Nn][Ii].*")) { // Starts with "uni" case and locale independent
1309-
isUniversity = true;
1310-
}
1311-
if (k.matches("^[Tt][Ee][Cc][Hh].*")) { // Starts with "tech" case and locale independent
1312-
isTechnology = true;
1313-
}
1314-
if (StandardField.SCHOOL.getName().equalsIgnoreCase(k)) {
1315-
isSchool = true;
1316-
}
1317-
if (k.matches("^[Dd][EeIi][Pp].*") || k.matches("^[Ll][Aa][Bb].*")) { // Starts with "dep"/"dip"/"lab", case and locale independent
1318-
isDepartment = true;
1319-
}
1320-
}
1321-
if (isTechnology) {
1322-
isUniversity = false; // technology institute isn't university :-)
1323-
}
1324-
1325-
// University part looks like: Uni[NameOfTheUniversity]
1326-
//
1327-
// If university is detected than the previous part is suggested
1328-
// as department
1329-
if (isUniversity) {
1341+
if (tokenTypes.contains(Institution.UNIVERSITY)) {
13301342
StringBuilder universitySB = new StringBuilder();
1343+
// University part looks like: Uni[NameOfTheUniversity]
13311344
universitySB.append("Uni");
1332-
for (String k : part) {
1333-
if (!k.matches("^[Uu][Nn][Ii].*")) {
1345+
for (String k : tokenParts) {
1346+
if (!"uni".regionMatches(true, 0, k, 0, 3)) {
13341347
universitySB.append(k);
13351348
}
13361349
}
13371350
university = universitySB.toString();
1351+
// If university is detected than the previous part is suggested
1352+
// as department
13381353
if ((index > 0) && (department == null)) {
1339-
department = parts[index - 1];
1354+
department = institutionNameTokens[index - 1];
13401355
}
1341-
1356+
} else if ((tokenTypes.contains(Institution.SCHOOL)
1357+
|| tokenTypes.contains(Institution.DEPARTMENT))
1358+
&& institutionNameTokens.length > 1) {
13421359
// School is an abbreviation of all the words beginning with a
13431360
// capital letter excluding: department, school and faculty words.
1344-
//
1345-
// Explicitly defined department part is build the same way as
1346-
// school
1347-
} else if (isSchool || isDepartment) {
13481361
StringBuilder schoolSB = new StringBuilder();
13491362
StringBuilder departmentSB = new StringBuilder();
1350-
for (String k : part) {
1351-
if (!k.matches("^[Dd][EeIi][Pp].*") && !StandardField.SCHOOL.getName().equalsIgnoreCase(k)
1352-
&& !"faculty".equalsIgnoreCase(k)
1353-
&& !(k.replaceAll(STARTING_CAPITAL_PATTERN, "").isEmpty())) {
1354-
if (isSchool) {
1355-
schoolSB.append(k.replaceAll(STARTING_CAPITAL_PATTERN, ""));
1363+
for (String k : tokenParts) {
1364+
if (noOtherInstitutionKeyWord(k)) {
1365+
if (tokenTypes.contains(Institution.SCHOOL)) {
1366+
schoolSB.append(NOT_CAPITAL_FIRST_CHARACTER.matcher(k).replaceAll(""));
13561367
}
1357-
if (isDepartment) {
1358-
departmentSB.append(k.replaceAll(STARTING_CAPITAL_PATTERN, ""));
1368+
// Explicitly defined department part is build the same way as school
1369+
if (tokenTypes.contains(Institution.DEPARTMENT)) {
1370+
departmentSB.append(NOT_CAPITAL_FIRST_CHARACTER.matcher(k).replaceAll(""));
13591371
}
13601372
}
13611373
}
1362-
if (isSchool) {
1374+
if (tokenTypes.contains(Institution.SCHOOL)) {
13631375
school = schoolSB.toString();
13641376
}
1365-
if (isDepartment) {
1377+
if (tokenTypes.contains(Institution.DEPARTMENT)) {
13661378
department = departmentSB.toString();
13671379
}
1368-
// A part not matching university, department nor school.
13691380
} else if (rest == null) {
1370-
StringBuilder restSB = new StringBuilder();
1371-
// Less than 3 parts -> concatenate those
1372-
if (part.size() < 3) {
1373-
for (String k : part) {
1374-
restSB.append(k);
1375-
// More than 3 parts -> use 1st letter abbreviation
1376-
}
1381+
// A part not matching university, department nor school
1382+
if (tokenParts.size() >= 3) {
1383+
// If there are more than 3 parts, only keep the first character of each word
1384+
final int[] codePoints = tokenParts.stream()
1385+
.filter(Predicate.not(String::isBlank))
1386+
.mapToInt((s) -> s.codePointAt(0))
1387+
.toArray();
1388+
rest = new String(codePoints, 0, codePoints.length);
13771389
} else {
1378-
for (String k : part) {
1379-
k = k.replaceAll(STARTING_CAPITAL_PATTERN, "");
1380-
if (!(k.isEmpty())) {
1381-
restSB.append(k);
1382-
}
1383-
}
1390+
rest = String.join("", tokenParts);
13841391
}
1385-
rest = restSB.toString();
13861392
}
13871393
}
13881394

13891395
// Putting parts together.
1390-
return (university == null ? rest : university)
1396+
return (university == null ? Objects.toString(rest, "") : university)
13911397
+ (school == null ? "" : school)
13921398
+ ((department == null)
13931399
|| ((school != null) && department.equals(school)) ? "" : department);
13941400
}
1401+
1402+
/**
1403+
* Checks that this is not an institution keyword and has an uppercase first letter, except univ/tech key word.
1404+
* @param word to check
1405+
* @return
1406+
*/
1407+
private static boolean noOtherInstitutionKeyWord(String word) {
1408+
return !DEPARTMENTS.matcher(word).matches()
1409+
&& !StandardField.SCHOOL.getName().equalsIgnoreCase(word)
1410+
&& !"faculty".equalsIgnoreCase(word)
1411+
&& !NOT_CAPITAL_FIRST_CHARACTER.matcher(word).replaceAll("").isEmpty();
1412+
}
1413+
1414+
private static List<String> getValidInstitutionNameParts(String name) {
1415+
List<String> nameParts = new ArrayList<>();
1416+
List<String> ignore = Arrays.asList("press", "the");
1417+
1418+
// Cleanup: remove unnecessary words.
1419+
for (String part : name.replaceAll("\\{[A-Z]+}", "").split("[ \\-_]")) {
1420+
if ((!(part.isEmpty()) // remove empty
1421+
&& !ignore.contains(part.toLowerCase(Locale.ENGLISH)) // remove ignored words
1422+
&& (part.charAt(part.length() - 1) != '.')
1423+
&& Character.isUpperCase(part.charAt(0)))
1424+
|| ((part.length() >= 3) && "uni".equalsIgnoreCase(part.substring(0, 3)))) {
1425+
nameParts.add(part);
1426+
}
1427+
}
1428+
return nameParts;
1429+
}
13951430
}

‎src/main/java/org/jabref/model/entry/AuthorList.java

+90-38
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import java.util.WeakHashMap;
1212
import java.util.stream.Collectors;
1313

14+
import org.jabref.model.strings.LatexToUnicodeAdapter;
15+
1416
/**
1517
* This is an immutable class representing information of either <CODE>author</CODE>
1618
* or <CODE>editor</CODE> field in bibtex record.
@@ -125,14 +127,18 @@ public class AuthorList {
125127
private final static Collection<String> AVOID_TERMS_IN_LOWER_CASE = Arrays.asList("jr", "sr", "jnr", "snr", "von", "zu", "van", "der");
126128
private final List<Author> authors;
127129
private final String[] authorsFirstFirst = new String[4];
130+
private final String[] authorsFirstFirstLatexFree = new String[4];
128131
private final String[] authorsLastOnly = new String[2];
132+
private final String[] authorsLastOnlyLatexFree = new String[2];
129133
private final String[] authorLastFirstAnds = new String[2];
130134
private final String[] authorsLastFirst = new String[4];
135+
private final String[] authorsLastFirstLatexFree = new String[4];
131136
private final String[] authorsLastFirstFirstLast = new String[2];
132137
// Variables for storing computed strings, so they only need to be created once:
133138
private String authorsNatbib;
134139
private String authorsFirstFirstAnds;
135140
private String authorsAlph;
141+
private String authorsNatbibLatexFree;
136142

137143
/**
138144
* Creates a new list of authors.
@@ -223,8 +229,8 @@ public static AuthorList parse(String authors) {
223229
*
224230
* @see AuthorList#getAsFirstLastNames
225231
*/
226-
public static String fixAuthorFirstNameFirstCommas(String authors, boolean abbr, boolean oxfordComma) {
227-
return AuthorList.parse(authors).getAsFirstLastNames(abbr, oxfordComma);
232+
public static String fixAuthorFirstNameFirstCommas(String authors, boolean abbreviate, boolean oxfordComma) {
233+
return AuthorList.parse(authors).getAsFirstLastNames(abbreviate, oxfordComma);
228234
}
229235

230236
/**
@@ -241,8 +247,8 @@ public static String fixAuthorFirstNameFirst(String authors) {
241247
*
242248
* @see AuthorList#getAsLastFirstNames
243249
*/
244-
public static String fixAuthorLastNameFirstCommas(String authors, boolean abbr, boolean oxfordComma) {
245-
return AuthorList.parse(authors).getAsLastFirstNames(abbr, oxfordComma);
250+
public static String fixAuthorLastNameFirstCommas(String authors, boolean abbreviate, boolean oxfordComma) {
251+
return AuthorList.parse(authors).getAsLastFirstNames(abbreviate, oxfordComma);
246252
}
247253

248254
/**
@@ -390,6 +396,15 @@ public String getAsNatbib() {
390396
return authorsNatbib;
391397
}
392398

399+
public String getAsNatbibLatexFree() {
400+
// Check if we've computed this before:
401+
if (authorsNatbibLatexFree != null) {
402+
return authorsNatbibLatexFree;
403+
}
404+
authorsNatbibLatexFree = LatexToUnicodeAdapter.format(getAsNatbib());
405+
return authorsNatbibLatexFree;
406+
}
407+
393408
/**
394409
* Returns the list of authors separated by commas with last name only; If
395410
* the list consists of two or more authors, "and" is inserted before the
@@ -409,11 +424,11 @@ public String getAsNatbib() {
409424
* Oxford comma.</a>
410425
*/
411426
public String getAsLastNames(boolean oxfordComma) {
412-
int abbrInt = oxfordComma ? 0 : 1;
427+
int abbreviationIndex = oxfordComma ? 0 : 1;
413428

414429
// Check if we've computed this before:
415-
if (authorsLastOnly[abbrInt] != null) {
416-
return authorsLastOnly[abbrInt];
430+
if (authorsLastOnly[abbreviationIndex] != null) {
431+
return authorsLastOnly[abbreviationIndex];
417432
}
418433

419434
StringBuilder result = new StringBuilder();
@@ -433,8 +448,19 @@ public String getAsLastNames(boolean oxfordComma) {
433448
result.append(getAuthor(i).getLastOnly());
434449
}
435450
}
436-
authorsLastOnly[abbrInt] = result.toString();
437-
return authorsLastOnly[abbrInt];
451+
authorsLastOnly[abbreviationIndex] = result.toString();
452+
return authorsLastOnly[abbreviationIndex];
453+
}
454+
455+
public String getAsLastNamesLatexFree(boolean oxfordComma) {
456+
int abbreviationIndex = oxfordComma ? 0 : 1;
457+
458+
// Check if we've computed this before:
459+
if (authorsLastOnlyLatexFree[abbreviationIndex] != null) {
460+
return authorsLastOnlyLatexFree[abbreviationIndex];
461+
}
462+
authorsLastOnlyLatexFree[abbreviationIndex] = LatexToUnicodeAdapter.format(getAsLastNames(oxfordComma));
463+
return authorsLastOnlyLatexFree[abbreviationIndex];
438464
}
439465

440466
/**
@@ -460,12 +486,12 @@ public String getAsLastNames(boolean oxfordComma) {
460486
* Oxford comma.</a>
461487
*/
462488
public String getAsLastFirstNames(boolean abbreviate, boolean oxfordComma) {
463-
int abbrInt = abbreviate ? 0 : 1;
464-
abbrInt += oxfordComma ? 0 : 2;
489+
int abbreviationIndex = abbreviate ? 0 : 1;
490+
abbreviationIndex += oxfordComma ? 0 : 2;
465491

466492
// Check if we've computed this before:
467-
if (authorsLastFirst[abbrInt] != null) {
468-
return authorsLastFirst[abbrInt];
493+
if (authorsLastFirst[abbreviationIndex] != null) {
494+
return authorsLastFirst[abbreviationIndex];
469495
}
470496

471497
StringBuilder result = new StringBuilder();
@@ -485,8 +511,21 @@ public String getAsLastFirstNames(boolean abbreviate, boolean oxfordComma) {
485511
result.append(getAuthor(i).getLastFirst(abbreviate));
486512
}
487513
}
488-
authorsLastFirst[abbrInt] = result.toString();
489-
return authorsLastFirst[abbrInt];
514+
authorsLastFirst[abbreviationIndex] = result.toString();
515+
return authorsLastFirst[abbreviationIndex];
516+
}
517+
518+
public String getAsLastFirstNamesLatexFree(boolean abbreviate, boolean oxfordComma) {
519+
int abbreviationIndex = abbreviate ? 0 : 1;
520+
abbreviationIndex += oxfordComma ? 0 : 2;
521+
522+
// Check if we've computed this before:
523+
if (authorsLastFirstLatexFree[abbreviationIndex] != null) {
524+
return authorsLastFirstLatexFree[abbreviationIndex];
525+
}
526+
527+
authorsLastFirstLatexFree[abbreviationIndex] = LatexToUnicodeAdapter.format(getAsLastFirstNames(abbreviate, oxfordComma));
528+
return authorsLastFirstLatexFree[abbreviationIndex];
490529
}
491530

492531
@Override
@@ -509,22 +548,22 @@ public String toString() {
509548
* @return formatted list of authors.
510549
*/
511550
public String getAsLastFirstNamesWithAnd(boolean abbreviate) {
512-
int abbrInt = abbreviate ? 0 : 1;
551+
int abbreviationIndex = abbreviate ? 0 : 1;
513552
// Check if we've computed this before:
514-
if (authorLastFirstAnds[abbrInt] != null) {
515-
return authorLastFirstAnds[abbrInt];
553+
if (authorLastFirstAnds[abbreviationIndex] != null) {
554+
return authorLastFirstAnds[abbreviationIndex];
516555
}
517556

518-
authorLastFirstAnds[abbrInt] = getAuthors().stream().map(author -> author.getLastFirst(abbreviate))
557+
authorLastFirstAnds[abbreviationIndex] = getAuthors().stream().map(author -> author.getLastFirst(abbreviate))
519558
.collect(Collectors.joining(" and "));
520-
return authorLastFirstAnds[abbrInt];
559+
return authorLastFirstAnds[abbreviationIndex];
521560
}
522561

523562
public String getAsLastFirstFirstLastNamesWithAnd(boolean abbreviate) {
524-
int abbrInt = abbreviate ? 0 : 1;
563+
int abbreviationIndex = abbreviate ? 0 : 1;
525564
// Check if we've computed this before:
526-
if (authorsLastFirstFirstLast[abbrInt] != null) {
527-
return authorsLastFirstFirstLast[abbrInt];
565+
if (authorsLastFirstFirstLast[abbreviationIndex] != null) {
566+
return authorsLastFirstFirstLast[abbreviationIndex];
528567
}
529568

530569
StringBuilder result = new StringBuilder();
@@ -536,8 +575,8 @@ public String getAsLastFirstFirstLastNamesWithAnd(boolean abbreviate) {
536575
}
537576
}
538577

539-
authorsLastFirstFirstLast[abbrInt] = result.toString();
540-
return authorsLastFirstFirstLast[abbrInt];
578+
authorsLastFirstFirstLast[abbreviationIndex] = result.toString();
579+
return authorsLastFirstFirstLast[abbreviationIndex];
541580
}
542581

543582
/**
@@ -555,41 +594,54 @@ public String getAsLastFirstFirstLastNamesWithAnd(boolean abbreviate) {
555594
* Smith and P. Black Brown" </li>
556595
* </ul>
557596
*
558-
* @param abbr whether to abbreivate first names.
597+
* @param abbreviate whether to abbreivate first names.
559598
* @param oxfordComma Whether to put a comma before the and at the end.
560599
* @return formatted list of authors.
561600
* @see <a href="http://en.wikipedia.org/wiki/Serial_comma">serial comma for an detailed explaination about the
562601
* Oxford comma.</a>
563602
*/
564-
public String getAsFirstLastNames(boolean abbr, boolean oxfordComma) {
603+
public String getAsFirstLastNames(boolean abbreviate, boolean oxfordComma) {
565604

566-
int abbrInt = abbr ? 0 : 1;
567-
abbrInt += oxfordComma ? 0 : 2;
605+
int abbreviationIndex = abbreviate ? 0 : 1;
606+
abbreviationIndex += oxfordComma ? 0 : 2;
568607

569608
// Check if we've computed this before:
570-
if (authorsFirstFirst[abbrInt] != null) {
571-
return authorsFirstFirst[abbrInt];
609+
if (authorsFirstFirst[abbreviationIndex] != null) {
610+
return authorsFirstFirst[abbreviationIndex];
572611
}
573612

574613
StringBuilder result = new StringBuilder();
575614
if (!isEmpty()) {
576-
result.append(getAuthor(0).getFirstLast(abbr));
615+
result.append(getAuthor(0).getFirstLast(abbreviate));
577616
int i = 1;
578617
while (i < (getNumberOfAuthors() - 1)) {
579618
result.append(", ");
580-
result.append(getAuthor(i).getFirstLast(abbr));
619+
result.append(getAuthor(i).getFirstLast(abbreviate));
581620
i++;
582621
}
583622
if ((getNumberOfAuthors() > 2) && oxfordComma) {
584623
result.append(',');
585624
}
586625
if (getNumberOfAuthors() > 1) {
587626
result.append(" and ");
588-
result.append(getAuthor(i).getFirstLast(abbr));
627+
result.append(getAuthor(i).getFirstLast(abbreviate));
589628
}
590629
}
591-
authorsFirstFirst[abbrInt] = result.toString();
592-
return authorsFirstFirst[abbrInt];
630+
authorsFirstFirst[abbreviationIndex] = result.toString();
631+
return authorsFirstFirst[abbreviationIndex];
632+
}
633+
634+
public String getAsFirstLastNamesLatexFree(boolean abbreviate, boolean oxfordComma) {
635+
int abbreviationIndex = abbreviate ? 0 : 1;
636+
abbreviationIndex += oxfordComma ? 0 : 2;
637+
638+
// Check if we've computed this before:
639+
if (authorsFirstFirstLatexFree[abbreviationIndex] != null) {
640+
return authorsFirstFirstLatexFree[abbreviationIndex];
641+
}
642+
643+
authorsFirstFirstLatexFree[abbreviationIndex] = LatexToUnicodeAdapter.format(getAsFirstLastNames(abbreviate, oxfordComma));
644+
return authorsFirstFirstLatexFree[abbreviationIndex];
593645
}
594646

595647
/**
@@ -660,7 +712,7 @@ public String getForAlphabetization() {
660712
return authorsAlph;
661713
}
662714

663-
public void addAuthor(String first, String firstabbr, String von, String last, String jr) {
664-
authors.add(new Author(first, firstabbr, von, last, jr));
715+
public void addAuthor(String first, String firstNameAbbreviation, String von, String last, String jr) {
716+
authors.add(new Author(first, firstNameAbbreviation, von, last, jr));
665717
}
666718
}

‎src/test/java/org/jabref/logic/bibtexkeypattern/BibtexKeyGeneratorTest.java

+126-90
Large diffs are not rendered by default.

‎src/test/java/org/jabref/model/entry/AuthorListTest.java

+518-37
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.