package org.jabref.logic.layout.format;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import org.jabref.logic.layout.AbstractParamLayoutFormatter;
import org.jabref.model.entry.Author;
import org.jabref.model.entry.AuthorList;
/**
* Versatile author name formatter that takes arguments to control the formatting style.
*/
public class Authors extends AbstractParamLayoutFormatter {
/*
AuthorSort = [FirstFirst | LastFirst | LastFirstFirstFirst]
AuthorAbbr = [FullName | Initials | FirstInitial | MiddleInitial | InitialsNoSpace | LastName]
AuthorSep = [Comma | And | Colon | Semicolon | Sep=<string>]
AuthorLastSep = [And | Comma | Colon | Semicolon | Amp | Oxford | LastSep=<string>]
AuthorPunc = [FullPunc | NoPunc | NoComma | NoPeriod]
AuthorNumber = [inf | <number>]
AuthorNumberEtAl = [ {1} | <number>]
EtAlString = [ et al. | EtAl=<string>]
*/
private static final List<String> AUTHOR_ORDER = new ArrayList<>();
private static final List<String> AUTHOR_ABRV = new ArrayList<>();
private static final List<String> AUTHOR_PUNC = new ArrayList<>();
private static final List<String> SEPARATORS = new ArrayList<>();
private static final List<String> LAST_SEPARATORS = new ArrayList<>();
private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+");
static {
Authors.AUTHOR_ORDER.add("firstfirst");
Authors.AUTHOR_ORDER.add("lastfirst");
Authors.AUTHOR_ORDER.add("lastfirstfirstfirst");
Authors.AUTHOR_ABRV.add("fullname");
Authors.AUTHOR_ABRV.add("initials");
Authors.AUTHOR_ABRV.add("firstinitial");
Authors.AUTHOR_ABRV.add("middleinitial");
Authors.AUTHOR_ABRV.add("lastname");
Authors.AUTHOR_ABRV.add("initialsnospace");
Authors.AUTHOR_PUNC.add("fullpunc");
Authors.AUTHOR_PUNC.add("nopunc");
Authors.AUTHOR_PUNC.add("nocomma");
Authors.AUTHOR_PUNC.add("noperiod");
Authors.SEPARATORS.add("comma");
Authors.SEPARATORS.add("and");
Authors.SEPARATORS.add("colon");
Authors.SEPARATORS.add("semicolon");
Authors.SEPARATORS.add("sep");
Authors.LAST_SEPARATORS.add("and");
Authors.LAST_SEPARATORS.add("colon");
Authors.LAST_SEPARATORS.add("semicolon");
Authors.LAST_SEPARATORS.add("amp");
Authors.LAST_SEPARATORS.add("oxford");
Authors.LAST_SEPARATORS.add("lastsep");
}
private static final int
FIRST_FIRST = 0;
private static final int LAST_FIRST = 1;
private static final int LF_FF = 2;
private static final String
COMMA = ", ";
private static final String AMP = " & ";
private static final String COLON = ": ";
private static final String SEMICOLON = "; ";
private static final String AND = " and ";
private static final String OXFORD = ", and ";
private int flMode;
private boolean
abbreviate = true;
private boolean firstInitialOnly;
private boolean middleInitial;
private boolean lastNameOnly;
private boolean abbrDots = true;
private boolean abbrSpaces = true;
private boolean setSep;
private boolean setMaxAuthors;
private int maxAuthors = -1;
private int authorNumberEtAl = 1;
private String lastFirstSeparator = ", ";
private String separator = Authors.COMMA;
private String lastSeparator = Authors.AND;
private String etAlString = " et al.";
@Override
public void setArgument(String arg) {
List<String> parts = AbstractParamLayoutFormatter.parseArgument(arg);
for (String part : parts) {
int index = part.indexOf('=');
if (index > 0) {
String key = part.substring(0, index);
String value = part.substring(index + 1);
handleArgument(key, value);
} else {
handleArgument(part, "");
}
}
}
private void handleArgument(String key, String value) {
if (Authors.AUTHOR_ORDER.contains(key.trim().toLowerCase(Locale.ROOT))) {
if (comp(key, "FirstFirst")) {
flMode = Authors.FIRST_FIRST;
} else if (comp(key, "LastFirst")) {
flMode = Authors.LAST_FIRST;
} else if (comp(key, "LastFirstFirstFirst")) {
flMode = Authors.LF_FF;
}
} else if (Authors.AUTHOR_ABRV.contains(key.trim().toLowerCase(Locale.ROOT))) {
if (comp(key, "FullName")) {
abbreviate = false;
} else if (comp(key, "Initials")) {
abbreviate = true;
firstInitialOnly = false;
} else if (comp(key, "FirstInitial")) {
abbreviate = true;
firstInitialOnly = true;
} else if (comp(key, "MiddleInitial")) {
abbreviate = true;
middleInitial = true;
} else if (comp(key, "LastName")) {
lastNameOnly = true;
} else if (comp(key, "InitialsNoSpace")) {
abbreviate = true;
abbrSpaces = false;
}
} else if (Authors.AUTHOR_PUNC.contains(key.trim().toLowerCase(Locale.ROOT))) {
if (comp(key, "FullPunc")) {
abbrDots = true;
lastFirstSeparator = ", ";
} else if (comp(key, "NoPunc")) {
abbrDots = false;
lastFirstSeparator = " ";
} else if (comp(key, "NoComma")) {
abbrDots = true;
lastFirstSeparator = " ";
} else if (comp(key, "NoPeriod")) {
abbrDots = false;
lastFirstSeparator = ", ";
}
}
// AuthorSep = [Comma | And | Colon | Semicolon | sep=<string>]
// AuthorLastSep = [And | Comma | Colon | Semicolon | Amp | Oxford | lastsep=<string>]
else if (Authors.SEPARATORS.contains(key.trim().toLowerCase(Locale.ROOT)) || Authors.LAST_SEPARATORS.contains(key.trim().toLowerCase(Locale.ROOT))) {
if (comp(key, "Comma")) {
if (setSep) {
lastSeparator = Authors.COMMA;
} else {
separator = Authors.COMMA;
setSep = true;
}
} else if (comp(key, "And")) {
if (setSep) {
lastSeparator = Authors.AND;
} else {
separator = Authors.AND;
setSep = true;
}
} else if (comp(key, "Colon")) {
if (setSep) {
lastSeparator = Authors.COLON;
} else {
separator = Authors.COLON;
setSep = true;
}
} else if (comp(key, "Semicolon")) {
if (setSep) {
lastSeparator = Authors.SEMICOLON;
} else {
separator = Authors.SEMICOLON;
setSep = true;
}
} else if (comp(key, "Oxford")) {
lastSeparator = Authors.OXFORD;
} else if (comp(key, "Amp")) {
lastSeparator = Authors.AMP;
} else if (comp(key, "Sep") && !value.isEmpty()) {
separator = value;
setSep = true;
} else if (comp(key, "LastSep") && !value.isEmpty()) {
lastSeparator = value;
}
} else if ("etal".equalsIgnoreCase(key.trim())) {
etAlString = value;
} else if (Authors.NUMBER_PATTERN.matcher(key.trim()).matches()) {
// Just a number:
int num = Integer.parseInt(key.trim());
if (setMaxAuthors) {
authorNumberEtAl = num;
} else {
maxAuthors = num;
setMaxAuthors = true;
}
}
}
/**
* Check for case-insensitive equality between two strings after removing
* white space at the beginning and end of the first string.
* @param one The first string - whitespace is trimmed
* @param two The second string
* @return true if the strings are deemed equal
*/
private static boolean comp(String one, String two) {
return one.trim().equalsIgnoreCase(two);
}
@Override
public String format(String fieldText) {
if (fieldText == null) {
return "";
}
StringBuilder sb = new StringBuilder();
AuthorList al = AuthorList.parse(fieldText);
if ((maxAuthors < 0) || (al.getNumberOfAuthors() <= maxAuthors)) {
for (int i = 0; i < al.getNumberOfAuthors(); i++) {
Author a = al.getAuthor(i);
addSingleName(sb, a, (flMode == Authors.FIRST_FIRST) || ((flMode == Authors.LF_FF) && (i > 0)));
if (i < (al.getNumberOfAuthors() - 2)) {
sb.append(separator);
} else if (i < (al.getNumberOfAuthors() - 1)) {
sb.append(lastSeparator);
}
}
} else {
for (int i = 0; i < Math.min(al.getNumberOfAuthors() - 1, authorNumberEtAl); i++) {
if (i > 0) {
sb.append(separator);
}
addSingleName(sb, al.getAuthor(i), flMode == Authors.FIRST_FIRST);
}
sb.append(etAlString);
}
return sb.toString();
}
private void addSingleName(StringBuilder sb, Author a, boolean firstFirst) {
StringBuilder lastNameSB = new StringBuilder();
a.getVon().filter(von -> !von.isEmpty()).ifPresent(von -> lastNameSB.append(von).append(' '));
a.getLast().ifPresent(lastNameSB::append);
String jrSeparator = " ";
a.getJr().filter(jr -> !jr.isEmpty()).ifPresent(jr -> lastNameSB.append(jrSeparator).append(jr));
String firstNameResult = "";
if (a.getFirst().isPresent()) {
if (abbreviate) {
firstNameResult = a.getFirstAbbr().orElse("");
if (firstInitialOnly && (firstNameResult.length() > 2)) {
firstNameResult = firstNameResult.substring(0, 2);
} else if (middleInitial) {
String abbr = firstNameResult;
firstNameResult = a.getFirst().get();
int index = firstNameResult.indexOf(' ');
//System.out.println(firstNamePart);
//System.out.println(index);
if (index >= 0) {
firstNameResult = firstNameResult.substring(0, index + 1);
if (abbr.length() > 3) {
firstNameResult = firstNameResult + abbr.substring(3);
}
}
}
if (!abbrDots) {
firstNameResult = firstNameResult.replace(".", "");
}
if (!abbrSpaces) {
firstNameResult = firstNameResult.replace(" ", "");
}
} else {
firstNameResult = a.getFirst().get();
}
}
if (lastNameOnly || (firstNameResult.isEmpty())) {
sb.append(lastNameSB);
} else if (firstFirst) {
String firstFirstSeparator = " ";
sb.append(firstNameResult).append(firstFirstSeparator);
sb.append(lastNameSB);
} else {
sb.append(lastNameSB).append(lastFirstSeparator).append(firstNameResult);
}
}
}