diff --git a/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java b/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java index fc02dc44e3e..b2fa0f5221a 100644 --- a/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java +++ b/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java @@ -11,15 +11,18 @@ import java.util.Scanner; import java.util.StringJoiner; import java.util.StringTokenizer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.jabref.logic.cleanup.Formatter; import org.jabref.logic.formatter.Formatters; import org.jabref.logic.formatter.casechanger.Word; import org.jabref.logic.layout.format.RemoveLatexCommandsFormatter; import org.jabref.model.database.BibDatabase; +import org.jabref.model.entry.Author; import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.Keyword; @@ -27,45 +30,65 @@ import org.jabref.model.entry.field.FieldFactory; import org.jabref.model.entry.field.InternalField; import org.jabref.model.entry.field.StandardField; -import org.jabref.model.strings.StringUtil; +import org.jabref.model.strings.LatexToUnicodeAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.jabref.logic.citationkeypattern.CitationKeyGenerator.DEFAULT_UNWANTED_CHARACTERS; - /** - * The BracketedExpressionExpander provides methods to expand bracketed expressions, - * such as [year]_[author]_[firstpage], using information from a provided BibEntry. - * The above-mentioned expression would yield 2017_Kitsune_123 when expanded using the - * BibTeX entry "@Article{ authors = {O. Kitsune}, year = {2017}, pages={123-6}}". + * The BracketedExpressionExpander provides methods to expand bracketed expressions, such as + * [year]_[author]_[firstpage], using information from a provided BibEntry. The above-mentioned expression would yield + * 2017_Kitsune_123 when expanded using the BibTeX entry "@Article{ authors = {O. Kitsune}, year = {2017}, + * pages={123-6}}". */ public class BracketedPattern { private static final Logger LOGGER = LoggerFactory.getLogger(BracketedPattern.class); + /** + * The maximum number of characters in the first author's last name. + */ private static final int CHARS_OF_FIRST = 5; + /** + * The maximum number of name abbreviations that can be used. If there are more authors, {@code MAX_ALPHA_AUTHORS - + * 1} name abbreviations will be displayed, and a + sign will be appended at the end. + */ + private static final int MAX_ALPHA_AUTHORS = 4; - /** Matches everything that is not an uppercase ASCII letter */ - private static final Pattern NOT_CAPITAL_FIRST_CHARACTER = Pattern.compile("[^A-Z]"); - /** Matches with "({[A-Z]}+)", which should be used to abbreviate the name of an institution */ + /** + * Matches everything that is not an uppercase ASCII letter. The intended use is to remove all lowercase letters + */ + private static final Pattern NOT_CAPITAL_CHARACTER = Pattern.compile("[^A-Z]"); + /** + * Matches with "({[A-Z]}+)", which should be used to abbreviate the name of an institution + */ private static final Pattern ABBREVIATIONS = Pattern.compile(".*\\(\\{[A-Z]+}\\).*"); - /** Matches with "dep"/"dip", case insensitive */ + /** + * Matches with "dep"/"dip", case insensitive + */ private static final Pattern DEPARTMENTS = Pattern.compile("^d[ei]p.*", Pattern.CASE_INSENSITIVE); + private enum Institution { SCHOOL, DEPARTMENT, UNIVERSITY, TECHNOLOGY; - /** Matches "uni" at the start of a string or after a space, case insensitive */ + /** + * Matches "uni" at the start of a string or after a space, case insensitive + */ private static final Pattern UNIVERSITIES = Pattern.compile("^uni.*", Pattern.CASE_INSENSITIVE); - /** Matches with "tech", case insensitive */ + /** + * Matches with "tech", case insensitive + */ private static final Pattern TECHNOLOGICAL_INSTITUTES = Pattern.compile("^tech.*", Pattern.CASE_INSENSITIVE); - /** Matches with "dep"/"dip"/"lab", case insensitive */ + /** + * Matches with "dep"/"dip"/"lab", case insensitive + */ private static final Pattern DEPARTMENTS_OR_LABS = Pattern.compile("^(d[ei]p|lab).*", Pattern.CASE_INSENSITIVE); /** * Find which types of institutions have words in common with the given name parts. + * * @param nameParts a list of words that constitute parts of an institution's name. * @return set containing all types that matches */ @@ -108,8 +131,7 @@ public String toString() { } public String expand(BibEntry bibentry) { - BibDatabase null_database = null; - return expand(bibentry, null_database); + return expand(bibentry, null); } /** @@ -150,197 +172,199 @@ public String expand(BibEntry bibentry, Character keywordDelimiter, BibDatabase public static String expandBrackets(String pattern, Character keywordDelimiter, BibEntry entry, BibDatabase database) { Objects.requireNonNull(pattern); Objects.requireNonNull(entry); - StringBuilder sb = new StringBuilder(); - StringTokenizer st = new StringTokenizer(pattern, "\\[]\"", true); - - while (st.hasMoreTokens()) { - String token = st.nextToken(); - if ("\"".equals(token)) { - sb.append(token); - while (st.hasMoreTokens()) { - token = st.nextToken(); - sb.append(token); - if ("\"".equals(token)) { - break; + StringBuilder expandedPattern = new StringBuilder(); + StringTokenizer parsedPattern = new StringTokenizer(pattern, "\\[]\"", true); + + while (parsedPattern.hasMoreTokens()) { + String token = parsedPattern.nextToken(); + switch (token) { + case "\"" -> appendQuote(expandedPattern, parsedPattern); + case "[" -> { + String fieldMarker = contentBetweenBrackets(parsedPattern, pattern); + + List fieldParts = parseFieldMarker(fieldMarker); + // check whether there is a modifier on the end such as + // ":lower": + if (fieldParts.size() <= 1) { + expandedPattern.append(getFieldValue(entry, fieldMarker, keywordDelimiter, database)); + } else { + // apply modifiers: + String fieldValue = getFieldValue(entry, fieldParts.get(0), keywordDelimiter, database); + expandedPattern.append(applyModifiers(fieldValue, fieldParts, 1)); } } - } else { - if ("\\".equals(token)) { - if (st.hasMoreTokens()) { - sb.append(st.nextToken()); + case "\\" -> { + if (parsedPattern.hasMoreTokens()) { + expandedPattern.append(parsedPattern.nextToken()); } // FIXME: else -> raise exception or log? (S.G.) - } else { - if ("[".equals(token)) { - Boolean foundClosingBracket = false; - // Fetch the next token after the '[': - token = st.nextToken(); - if ("]".equals(token)) { - LOGGER.warn("Found empty brackets \"[]\" in '" + pattern + "'"); - foundClosingBracket = true; - } - // make sure to read until the next ']' - while (st.hasMoreTokens() && !foundClosingBracket) { - String subtoken = st.nextToken(); - // I the beginning of a quote is found, include the content in the original token - if ("\"".equals(subtoken)) { - token = token + subtoken; - while (st.hasMoreTokens()) { - subtoken = st.nextToken(); - token = token + subtoken; - if ("\"".equals(subtoken)) { - break; - } - } - } else { - if ("]".equals(subtoken)) { - foundClosingBracket = true; - break; - } else { - token = token + subtoken; - } - } - } - if (!foundClosingBracket) { - LOGGER.warn("Missing closing bracket ']' in '" + pattern + "'"); - } - List fieldParts = parseFieldMarker(token); - // check whether there is a modifier on the end such as - // ":lower": - if (fieldParts.size() <= 1) { - sb.append(getFieldValue(entry, token, keywordDelimiter, database)); - } else { - // apply modifiers: - String fieldValue = getFieldValue(entry, fieldParts.get(0), keywordDelimiter, database); - sb.append(applyModifiers(fieldValue, fieldParts, 1)); - } - } else { - sb.append(token); - } } + default -> expandedPattern.append(token); } } - return sb.toString(); + return expandedPattern.toString(); } /** - * Evaluates the given pattern ("value") to the given bibentry and database + * Returns the content enclosed between brackets, including enclosed quotes, and excluding the enclosing brackets. + * Intended to be used by {@link BracketedPattern#expandBrackets(String, Character, BibEntry, BibDatabase)} when a [ + * is encountered, and has been consumed, by the {@code StringTokenizer}. + * + * @param pattern pattern used by {@code expandBrackets}, used for logging + * @param tokenizer the tokenizer producing the tokens + * @return the content enclosed by brackets + */ + private static String contentBetweenBrackets(StringTokenizer tokenizer, final String pattern) { + StringBuilder bracketContent = new StringBuilder(); + boolean foundClosingBracket = false; + // make sure to read until the next ']' + while (tokenizer.hasMoreTokens() && !foundClosingBracket) { + String token = tokenizer.nextToken(); + // If the beginning of a quote is found, append the content + switch (token) { + case "\"" -> appendQuote(bracketContent, tokenizer); + case "]" -> foundClosingBracket = true; + default -> bracketContent.append(token); + } + } + + if (!foundClosingBracket) { + LOGGER.warn("Missing closing bracket ']' in '{}'", pattern); + } else if (bracketContent.length() == 0) { + LOGGER.warn("Found empty brackets \"[]\" in '{}'", pattern); + } + return bracketContent.toString(); + } + + /** + * Appends the content between, and including, two \" to the provided StringBuilder. Intended to be + * used by {@link BracketedPattern#expandBrackets(String, Character, BibEntry, BibDatabase)} when a \" is + * encountered by the StringTokenizer. + * + * @param stringBuilder the StringBuilder to which tokens will be appended + * @param tokenizer the tokenizer producing the tokens + */ + private static void appendQuote(StringBuilder stringBuilder, StringTokenizer tokenizer) { + stringBuilder.append("\""); // We know that the previous token was \" + String token = ""; + while (tokenizer.hasMoreTokens() && !"\"".equals(token)) { + token = tokenizer.nextToken(); + stringBuilder.append(token); + } + } + + /** + * Evaluates the given pattern to the given bibentry and database * * @param entry The entry to get the field value from - * @param value A pattern string (such as auth, pureauth, authorLast) + * @param pattern A pattern string (such as auth, pureauth, authorLast) * @param keywordDelimiter The de * @param database The database to use for field resolving. May be null. * @return String containing the evaluation result. Empty string if the pattern cannot be resolved. */ - public static String getFieldValue(BibEntry entry, String value, Character keywordDelimiter, BibDatabase database) { - - String val = value; + public static String getFieldValue(BibEntry entry, String pattern, Character keywordDelimiter, BibDatabase database) { try { - if (val.startsWith("auth") || val.startsWith("pureauth")) { + if (pattern.startsWith("auth") || pattern.startsWith("pureauth")) { // result the author - String authString; - if (database != null) { - authString = entry.getResolvedFieldOrAlias(StandardField.AUTHOR, database) - .map(authorString -> normalize(database.resolveForStrings(authorString))).orElse(""); - } else { - authString = entry.getResolvedFieldOrAlias(StandardField.AUTHOR, database).orElse(""); - } + String unparsedAuthors = entry.getResolvedFieldOrAlias(StandardField.AUTHOR, database).orElse(""); - if (val.startsWith("pure")) { + if (pattern.startsWith("pure")) { // "pure" is used in the context of authors to resolve to authors only and not fallback to editors // The other functionality of the pattern "ForeIni", ... is the same // Thus, remove the "pure" prefix so the remaining code in this section functions correctly // - val = val.substring(4); - } else { + pattern = pattern.substring(4); + } else if (unparsedAuthors.isEmpty()) { // special feature: A pattern starting with "auth" falls back to the editor - if (authString.isEmpty()) { - if (database != null) { - authString = entry.getResolvedFieldOrAlias(StandardField.EDITOR, database) - .map(authorString -> normalize(database.resolveForStrings(authorString))).orElse(""); - } else { - authString = entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse(""); - } - } + unparsedAuthors = entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse(""); } + AuthorList authorList = createAuthorList(unparsedAuthors); + // Gather all author-related checks, so we don't // have to check all the time. - if ("auth".equals(val)) { - return firstAuthor(authString); - } else if ("authForeIni".equals(val)) { - return firstAuthorForenameInitials(authString); - } else if ("authFirstFull".equals(val)) { - return firstAuthorVonAndLast(authString); - } else if ("authors".equals(val)) { - return allAuthors(authString); - } else if ("authorsAlpha".equals(val)) { - return authorsAlpha(authString); - } else if ("authorLast".equals(val)) { - // Last author's last name - return lastAuthor(authString); - } else if ("authorLastForeIni".equals(val)) { - return lastAuthorForenameInitials(authString); - } else if ("authorIni".equals(val)) { - return oneAuthorPlusIni(authString); - } else if (val.matches("authIni[\\d]+")) { - int num = Integer.parseInt(val.substring(7)); - return authIniN(authString, num); - } else if ("auth.auth.ea".equals(val)) { - return authAuthEa(authString); - } else if ("auth.etal".equals(val)) { - return authEtal(authString, ".", ".etal"); - } else if ("authEtAl".equals(val)) { - return authEtal(authString, "", "EtAl"); - } else if ("authshort".equals(val)) { - return authshort(authString); - } else if (val.matches("auth[\\d]+_[\\d]+")) { - String[] nums = val.substring(4).split("_"); - return authNofMth(authString, Integer.parseInt(nums[0]), + switch (pattern) { + case "auth": + return firstAuthor(authorList); + case "authForeIni": + return firstAuthorForenameInitials(authorList); + case "authFirstFull": + return firstAuthorVonAndLast(authorList); + case "authors": + return allAuthors(authorList); + case "authorsAlpha": + return authorsAlpha(authorList); + case "authorLast": + return lastAuthor(authorList); + case "authorLastForeIni": + return lastAuthorForenameInitials(authorList); + case "authorIni": + return oneAuthorPlusInitials(authorList); + case "auth.auth.ea": + return authAuthEa(authorList); + case "auth.etal": + return authEtal(authorList, ".", ".etal"); + case "authEtAl": + return authEtal(authorList, "", "EtAl"); + case "authshort": + return authshort(authorList); + } + + if (pattern.matches("authIni[\\d]+")) { + int num = Integer.parseInt(pattern.substring(7)); + return authIniN(authorList, num); + } else if (pattern.matches("auth[\\d]+_[\\d]+")) { + String[] nums = pattern.substring(4).split("_"); + return authNofMth(authorList, Integer.parseInt(nums[0]), Integer.parseInt(nums[1])); - } else if (val.matches("auth\\d+")) { + } else if (pattern.matches("auth\\d+")) { // authN. First N chars of the first author's last name. - int num = Integer.parseInt(val.substring(4)); - return authN(authString, num); - } else if (val.matches("authors\\d+")) { - return nAuthors(authString, Integer.parseInt(val.substring(7))); + int num = Integer.parseInt(pattern.substring(4)); + return authN(authorList, num); + } else if (pattern.matches("authors\\d+")) { + return nAuthors(authorList, Integer.parseInt(pattern.substring(7))); } else { // This "auth" business was a dead end, so just // use it literally: - return entry.getResolvedFieldOrAlias(FieldFactory.parseField(val), database).orElse(""); + return entry.getResolvedFieldOrAlias(FieldFactory.parseField(pattern), database).orElse(""); } - } else if (val.startsWith("ed")) { + } else if (pattern.startsWith("ed")) { // Gather all markers starting with "ed" here, so we // don't have to check all the time. - if ("edtr".equals(val)) { - return firstAuthor(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - } else if ("edtrForeIni".equals(val)) { - return firstAuthorForenameInitials(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - } else if ("editors".equals(val)) { - return allAuthors(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - // Last author's last name - } else if ("editorLast".equals(val)) { - return lastAuthor(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - } else if ("editorLastForeIni".equals(val)) { - return lastAuthorForenameInitials(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - } else if ("editorIni".equals(val)) { - return oneAuthorPlusIni(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - } else if (val.matches("edtrIni[\\d]+")) { - int num = Integer.parseInt(val.substring(7)); - return authIniN(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse(""), num); - } else if (val.matches("edtr[\\d]+_[\\d]+")) { - String[] nums = val.substring(4).split("_"); - return authNofMth(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse(""), + String unparsedEditors = entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse(""); + AuthorList editorList = createAuthorList(unparsedEditors); + + switch (pattern) { + case "edtr": + return firstAuthor(editorList); + case "edtrForeIni": + return firstAuthorForenameInitials(editorList); + case "editors": + return allAuthors(editorList); + case "editorLast": + return lastAuthor(editorList); // Last author's last name + case "editorLastForeIni": + return lastAuthorForenameInitials(editorList); + case "editorIni": + return oneAuthorPlusInitials(editorList); + case "edtr.edtr.ea": + return authAuthEa(editorList); + case "edtrshort": + return authshort(editorList); + } + + if (pattern.matches("edtrIni[\\d]+")) { + int num = Integer.parseInt(pattern.substring(7)); + return authIniN(editorList, num); + } else if (pattern.matches("edtr[\\d]+_[\\d]+")) { + String[] nums = pattern.substring(4).split("_"); + return authNofMth(editorList, Integer.parseInt(nums[0]), Integer.parseInt(nums[1]) - 1); - } else if ("edtr.edtr.ea".equals(val)) { - return authAuthEa(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - } else if ("edtrshort".equals(val)) { - return authshort(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - } else if (val.matches("edtr\\d+")) { - String fa = firstAuthor(entry.getResolvedFieldOrAlias(StandardField.EDITOR, database).orElse("")); - int num = Integer.parseInt(val.substring(4)); + } else if (pattern.matches("edtr\\d+")) { + String fa = firstAuthor(editorList); + int num = Integer.parseInt(pattern.substring(4)); if (num > fa.length()) { num = fa.length(); } @@ -348,31 +372,31 @@ public static String getFieldValue(BibEntry entry, String value, Character keywo } else { // This "ed" business was a dead end, so just // use it literally: - return entry.getResolvedFieldOrAlias(FieldFactory.parseField(val), database).orElse(""); + return entry.getResolvedFieldOrAlias(FieldFactory.parseField(pattern), database).orElse(""); } - } else if ("firstpage".equals(val)) { + } else if ("firstpage".equals(pattern)) { return firstPage(entry.getResolvedFieldOrAlias(StandardField.PAGES, database).orElse("")); - } else if ("pageprefix".equals(val)) { + } else if ("pageprefix".equals(pattern)) { return pagePrefix(entry.getResolvedFieldOrAlias(StandardField.PAGES, database).orElse("")); - } else if ("lastpage".equals(val)) { + } else if ("lastpage".equals(pattern)) { return lastPage(entry.getResolvedFieldOrAlias(StandardField.PAGES, database).orElse("")); - } else if ("title".equals(val)) { + } else if ("title".equals(pattern)) { return camelizeSignificantWordsInTitle(entry.getResolvedFieldOrAlias(StandardField.TITLE, database).orElse("")); - } else if ("fulltitle".equals(val)) { + } else if ("fulltitle".equals(pattern)) { return entry.getResolvedFieldOrAlias(StandardField.TITLE, database).orElse(""); - } else if ("shorttitle".equals(val)) { + } else if ("shorttitle".equals(pattern)) { return getTitleWords(3, removeSmallWords(entry.getResolvedFieldOrAlias(StandardField.TITLE, database).orElse(""))); - } else if ("shorttitleINI".equals(val)) { + } else if ("shorttitleINI".equals(pattern)) { return keepLettersAndDigitsOnly( applyModifiers(getTitleWordsWithSpaces(3, entry.getResolvedFieldOrAlias(StandardField.TITLE, database).orElse("")), Collections.singletonList("abbr"), 0)); - } else if ("veryshorttitle".equals(val)) { + } else if ("veryshorttitle".equals(pattern)) { return getTitleWords(1, removeSmallWords(entry.getResolvedFieldOrAlias(StandardField.TITLE, database).orElse(""))); - } else if ("camel".equals(val)) { + } else if ("camel".equals(pattern)) { return getCamelizedTitle(entry.getResolvedFieldOrAlias(StandardField.TITLE, database).orElse("")); - } else if ("shortyear".equals(val)) { + } else if ("shortyear".equals(pattern)) { String yearString = entry.getResolvedFieldOrAlias(StandardField.YEAR, database).orElse(""); if (yearString.isEmpty()) { return yearString; @@ -384,11 +408,11 @@ public static String getFieldValue(BibEntry entry, String value, Character keywo } else { return yearString; } - } else if ("entrytype".equals(val)) { + } else if ("entrytype".equals(pattern)) { return entry.getResolvedFieldOrAlias(InternalField.TYPE_HEADER, database).orElse(""); - } else if (val.matches("keyword\\d+")) { + } else if (pattern.matches("keyword\\d+")) { // according to LabelPattern.php, it returns keyword number n - int num = Integer.parseInt(val.substring(7)); + int num = Integer.parseInt(pattern.substring(7)); KeywordList separatedKeywords = entry.getResolvedKeywords(keywordDelimiter, database); if (separatedKeywords.size() < num) { // not enough keywords @@ -397,11 +421,11 @@ public static String getFieldValue(BibEntry entry, String value, Character keywo // num counts from 1 to n, but index in arrayList count from 0 to n-1 return separatedKeywords.get(num - 1).toString(); } - } else if (val.matches("keywords\\d*")) { + } else if (pattern.matches("keywords\\d*")) { // return all keywords, not separated int num; - if (val.length() > 8) { - num = Integer.parseInt(val.substring(8)); + if (pattern.length() > 8) { + num = Integer.parseInt(pattern.substring(8)); } else { num = Integer.MAX_VALUE; } @@ -420,7 +444,7 @@ public static String getFieldValue(BibEntry entry, String value, Character keywo return sb.toString(); } else { // we haven't seen any special demands - return entry.getResolvedFieldOrAlias(FieldFactory.parseField(val), database).orElse(""); + return entry.getResolvedFieldOrAlias(FieldFactory.parseField(pattern), database).orElse(""); } } catch (NullPointerException ex) { LOGGER.debug("Problem making expanding bracketed expression", ex); @@ -428,6 +452,44 @@ public static String getFieldValue(BibEntry entry, String value, Character keywo } } + /** + * Parses the provided string to an {@link AuthorList}, which are then formatted by {@link LatexToUnicodeAdapter}. + * Afterward, any institutions are formatted into an institution key. + * + * @param unparsedAuthors a string representation of authors or editors + * @return an {@link AuthorList} consisting of authors and institution keys with resolved latex. + */ + private static AuthorList createAuthorList(String unparsedAuthors) { + AuthorList authorList = new AuthorList(); + for (Author author : AuthorList.parse(unparsedAuthors).getAuthors()) { + // If the author is an institution, use an institution key instead of the full name + String lastName = author.getLast() + .map(LatexToUnicodeAdapter::format) + .map(isInstitution(author) ? + BracketedPattern::generateInstitutionKey : Function.identity()) + .orElse(null); + authorList.addAuthor( + author.getFirst().map(LatexToUnicodeAdapter::format).orElse(null), + author.getFirstAbbr().map(LatexToUnicodeAdapter::format).orElse(null), + author.getVon().map(LatexToUnicodeAdapter::format).orElse(null), + lastName, + author.getJr().map(LatexToUnicodeAdapter::format).orElse(null) + ); + } + return authorList; + } + + /** + * Checks if an author is an institution by verifying that only the last name is present. + * + * @param author the checked author + * @return true if only the last name is present + */ + private static boolean isInstitution(Author author) { + return author.getFirst().isEmpty() && author.getFirstAbbr().isEmpty() && author.getJr().isEmpty() + && author.getVon().isEmpty() && author.getLast().isPresent(); + } + /** * Applies modifiers to a label generated based on a field marker. * @@ -462,7 +524,7 @@ static String applyModifiers(final String label, final List parts, final resultingLabel = modifier.substring(1, modifier.length() - 1); } } else { - LOGGER.warn("Key generator warning: unknown modifier '" + modifier + "'."); + LOGGER.warn("Key generator warning: unknown modifier '{}'.", modifier); } } } @@ -545,20 +607,14 @@ private static String camelizeTitle(String title) { public static String camelizeSignificantWordsInTitle(String title) { StringJoiner stringJoiner = new StringJoiner(" "); String formattedTitle = formatTitle(title); - Boolean camelize; try (Scanner titleScanner = new Scanner(formattedTitle)) { while (titleScanner.hasNext()) { String word = titleScanner.next(); - camelize = true; // Camelize the word if it is significant - for (String smallWord : Word.SMALLER_WORDS) { - if (word.equalsIgnoreCase(smallWord)) { - camelize = false; - continue; - } - } + boolean camelize = !Word.SMALLER_WORDS.contains(word.toLowerCase(Locale.ROOT)); + // We want to capitalize significant words and the first word of the title if (camelize || (stringJoiner.length() == 0)) { word = word.substring(0, 1).toUpperCase(Locale.ROOT) + word.substring(1); @@ -625,78 +681,64 @@ private static String keepLettersAndDigitsOnly(String in) { /** * Gets the last name of the first author/editor * - * @param authorField a String - * @return the surname of an author/editor or "" if no author was found This method is guaranteed to never return null. - * @throws NullPointerException if authorField == null + * @param authorList an {@link AuthorList} + * @return the surname of an author/editor or "" if no author was found This method is guaranteed to never return + * null. */ - public static String firstAuthor(String authorField) { - AuthorList authorList = AuthorList.parse(authorField); - if (authorList.isEmpty()) { - return ""; - } - return authorList.getAuthor(0).getLast().orElse(""); + private static String firstAuthor(AuthorList authorList) { + return authorList.getAuthors().stream() + .findFirst() + .flatMap(Author::getLast).orElse(""); } /** * Gets the first name initials of the first author/editor * - * @param authorField a String - * @return the first name initial of an author/editor or "" if no author was found This method is guaranteed to never return null. - * @throws NullPointerException if authorField == null + * @param authorList an {@link AuthorList} + * @return the first name initial of an author/editor or "" if no author was found This method is guaranteed to + * never return null. */ - public static String firstAuthorForenameInitials(String authorField) { - AuthorList authorList = AuthorList.parse(authorField); - if (authorList.isEmpty()) { - return ""; - } - return authorList.getAuthor(0).getFirstAbbr().map(s -> s.substring(0, 1)).orElse(""); + private static String firstAuthorForenameInitials(AuthorList authorList) { + return authorList.getAuthors().stream() + .findFirst() + .flatMap(Author::getFirstAbbr) + .map(s -> s.substring(0, 1)) + .orElse(""); } /** - * Gets the von part and the last name of the first author/editor No spaces are returned + * Gets the von part and the last name of the first author/editor. No spaces are returned. * - * @param authorField a String - * @return the von part and surname of an author/editor or "" if no author was found. This method is guaranteed to never return null. - * @throws NullPointerException if authorField == null + * @param authorList an {@link AuthorList} + * @return the von part and surname of an author/editor or "" if no author was found. This method is guaranteed to + * never return null. */ - public static String firstAuthorVonAndLast(String authorField) { - AuthorList authorList = AuthorList.parse(authorField); - if (authorList.isEmpty()) { - return ""; - } - - StringBuilder stringBuilder = new StringBuilder(); - authorList.getAuthor(0).getVon().ifPresent(vonAuthor -> stringBuilder.append(vonAuthor.replaceAll(" ", ""))); - authorList.getAuthor(0).getLast().ifPresent(stringBuilder::append); - return stringBuilder.toString(); + private static String firstAuthorVonAndLast(AuthorList authorList) { + return authorList.isEmpty() ? "" : + authorList.getAuthor(0).getLastOnly().replaceAll(" ", ""); } /** * Gets the last name of the last author/editor * - * @param authorField a String + * @param authorList an {@link AuthorList} * @return the surname of an author/editor */ - public static String lastAuthor(String authorField) { - String[] tokens = AuthorList.fixAuthorForAlphabetization(authorField).split("\\s+\\band\\b\\s+"); - if (tokens.length > 0) { - String[] lastAuthor = tokens[tokens.length - 1].split(","); - return lastAuthor[0]; - } else { - // if author is empty + private static String lastAuthor(AuthorList authorList) { + if (authorList.isEmpty()) { return ""; } + return authorList.getAuthors().get(authorList.getNumberOfAuthors() - 1).getLast().orElse(""); } /** * Gets the forename initials of the last author/editor * - * @param authorField a String - * @return the forename initial of an author/editor or "" if no author was found This method is guaranteed to never return null. - * @throws NullPointerException if authorField == null + * @param authorList an {@link AuthorList} + * @return the forename initial of an author/editor or "" if no author was found This method is guaranteed to never + * return null. */ - public static String lastAuthorForenameInitials(String authorField) { - AuthorList authorList = AuthorList.parse(authorField); + private static String lastAuthorForenameInitials(AuthorList authorList) { if (authorList.isEmpty()) { return ""; } @@ -707,243 +749,199 @@ public static String lastAuthorForenameInitials(String authorField) { /** * Gets the last name of all authors/editors * - * @param authorField a String + * @param authorList an {@link AuthorList} * @return the sur name of all authors/editors */ - public static String allAuthors(String authorField) { - // Quick hack to use NAuthors to avoid code duplication - return nAuthors(authorField, Integer.MAX_VALUE); + private static String allAuthors(AuthorList authorList) { + return joinAuthorsOnLastName(authorList, authorList.getNumberOfAuthors(), "", ""); } /** * Returns the authors according to the BibTeX-alpha-Style * - * @param authorField string containing the value of the author field - * @return the initials of all authornames + * @param authorList an {@link AuthorList} + * @return the initials of all authors' names */ - public static String authorsAlpha(String authorField) { - String authors = ""; - - String fixedAuthors = AuthorList.fixAuthorLastNameOnlyCommas(authorField, false); - - // drop the "and" before the last author - // -> makes processing easier - fixedAuthors = fixedAuthors.replace(" and ", ", "); - - String[] tokens = fixedAuthors.split(","); - int max = tokens.length > 4 ? 3 : tokens.length; - if (max == 1) { - String[] firstAuthor = tokens[0].replaceAll("\\s+", " ").trim().split(" "); + private static String authorsAlpha(AuthorList authorList) { + StringBuilder alphaStyle = new StringBuilder(); + int maxAuthors = authorList.getNumberOfAuthors() <= MAX_ALPHA_AUTHORS ? + authorList.getNumberOfAuthors() : (MAX_ALPHA_AUTHORS - 1); + + if (authorList.getNumberOfAuthors() == 1) { + String[] firstAuthor = authorList.getAuthor(0).getLastOnly() + .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++) { - authors = authors.concat(firstAuthor[j].substring(0, 1)); + alphaStyle.append(firstAuthor[j], 0, 1); } // append last part of last name completely - authors = authors.concat(firstAuthor[firstAuthor.length - 1].substring(0, - Math.min(3, firstAuthor[firstAuthor.length - 1].length()))); + alphaStyle.append(firstAuthor[firstAuthor.length - 1], 0, + Math.min(3, firstAuthor[firstAuthor.length - 1].length())); } else { - for (int i = 0; i < max; i++) { + List vonAndLastNames = authorList.getAuthors().stream() + .limit(maxAuthors).map(Author::getLastOnly) + .collect(Collectors.toList()); + for (String vonAndLast : vonAndLastNames) { // replace all whitespaces by " " // split the lastname at " " - String[] curAuthor = tokens[i].replaceAll("\\s+", " ").trim().split(" "); - for (String aCurAuthor : curAuthor) { + String[] nameParts = vonAndLast.replaceAll("\\s+", " ").trim().split(" "); + for (String part : nameParts) { // use first character of each part of lastname - authors = authors.concat(aCurAuthor.substring(0, 1)); + alphaStyle.append(part, 0, 1); } } - if (tokens.length > 4) { - authors = authors.concat("+"); + if (authorList.getNumberOfAuthors() > MAX_ALPHA_AUTHORS) { + alphaStyle.append("+"); } } - return authors; + return alphaStyle.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`. + * + * @param authorList the list of authors + * @param maxAuthors the maximum number of authors in the string + * @param delimiter delimiter separating the last names of the authors + * @param suffix to replace excess authors with + * @return a string consisting of authors' last names separated by a `delimiter` and with any authors excess of + * `maxAuthors` replaced with `suffix` + */ + private static String joinAuthorsOnLastName(AuthorList authorList, int maxAuthors, String delimiter, String suffix) { + suffix = authorList.getNumberOfAuthors() > maxAuthors ? suffix : ""; + return authorList.getAuthors().stream() + .map(Author::getLast).flatMap(Optional::stream) + .limit(maxAuthors).collect(Collectors.joining(delimiter, "", suffix)); } /** * Gets the surnames of the first N authors and appends EtAl if there are more than N authors * - * @param authorField a String - * @param n the number of desired authors + * @param authorList an {@link AuthorList} + * @param n the number of desired authors * @return Gets the surnames of the first N authors and appends EtAl if there are more than N authors */ - public static String nAuthors(String authorField, int n) { - String[] tokens = AuthorList.fixAuthorForAlphabetization(authorField).split("\\s+\\band\\b\\s+"); - int i = 0; - StringBuilder authorSB = new StringBuilder(); - while ((tokens.length > i) && (i < n)) { - String lastName = tokens[i].replaceAll(",\\s+.*", ""); - authorSB.append(lastName); - i++; - } - if (tokens.length > n) { - authorSB.append("EtAl"); - } - return authorSB.toString(); + private static String nAuthors(AuthorList authorList, int n) { + return joinAuthorsOnLastName(authorList, n, "", "EtAl"); } /** - * Gets the first part of the last name of the first - * author/editor, and appends the last name initial of the - * remaining authors/editors. - * Maximum 5 characters - * @param authorField a String + * Gets the first part of the last name of the first author/editor, and appends the last name initial of the + * remaining authors/editors. Maximum 5 characters + * + * @param authorList an <{@link AuthorList} * @return the surname of all authors/editors */ - public static String oneAuthorPlusIni(String authorField) { - String fixedAuthorField = AuthorList.fixAuthorForAlphabetization(authorField); - String[] tokens = fixedAuthorField.split("\\s+\\band\\b\\s+"); - if (tokens.length == 0) { + private static String oneAuthorPlusInitials(AuthorList authorList) { + if (authorList.isEmpty()) { return ""; } - String firstAuthor = tokens[0].split(",")[0]; StringBuilder authorSB = new StringBuilder(); - authorSB.append(firstAuthor, 0, Math.min(CHARS_OF_FIRST, firstAuthor.length())); - int i = 1; - while (tokens.length > i) { - // convert lastname, firstname to firstname lastname - authorSB.append(tokens[i].charAt(0)); - i++; + // authNofMth start index at 1 instead of 0 + authorSB.append(authNofMth(authorList, CHARS_OF_FIRST, 1)); + for (int i = 2; i <= authorList.getNumberOfAuthors(); i++) { + authorSB.append(authNofMth(authorList, 1, i)); } return authorSB.toString(); } /** * auth.auth.ea format: - * Isaac Newton and James Maxwell and Albert Einstein (1960) - * Isaac Newton and James Maxwell (1960) - * give: - * Newton.Maxwell.ea - * Newton.Maxwell + *
    + *
  1. Isaac Newton and James Maxwell and Albert Einstein (1960)
  2. + *
  3. Isaac Newton and James Maxwell (1960)
  4. + *
+ * give: + *
    + *
  1. Newton.Maxwell.ea
  2. + *
  3. Newton.Maxwell
  4. + *
*/ - public static String authAuthEa(String authorField) { - String fixedAuthorField = AuthorList.fixAuthorForAlphabetization(authorField); - - String[] tokens = fixedAuthorField.split("\\s+\\band\\b\\s+"); - if (tokens.length == 0) { - return ""; - } - - StringBuilder author = new StringBuilder(); - // append first author - author.append((tokens[0].split(","))[0]); - if (tokens.length >= 2) { - // append second author - author.append('.').append((tokens[1].split(","))[0]); - } - if (tokens.length > 2) { - // append ".ea" if more than 2 authors - author.append(".ea"); - } - - return author.toString(); + private static String authAuthEa(AuthorList authorList) { + return joinAuthorsOnLastName(authorList, 2, ".", ".ea"); } /** * auth.etal, authEtAl, ... format: - * Isaac Newton and James Maxwell and Albert Einstein (1960) - * Isaac Newton and James Maxwell (1960) - * - * auth.etal give (delim=".", append=".etal"): - * Newton.etal - * Newton.Maxwell - * - * authEtAl give (delim="", append="EtAl"): - * NewtonEtAl - * NewtonMaxwell - * + *
    + *
  1. Isaac Newton and James Maxwell and Albert Einstein (1960)
  2. + *
  3. Isaac Newton and James Maxwell (1960)
  4. + *
+ *

+ * auth.etal give (delim=".", append=".etal"): + *

    + *
  1. Newton.etal
  2. + *
  3. Newton.Maxwell
  4. + *
+ *

+ *

+ * authEtAl give (delim="", append="EtAl"): + *

    + *
  1. NewtonEtAl
  2. + *
  3. NewtonMaxwell
  4. + *
+ *

* Note that [authEtAl] equals [authors2] */ - public static String authEtal(String authorField, String delim, - String append) { - String fixedAuthorField = AuthorList.fixAuthorForAlphabetization(authorField); - - String[] tokens = fixedAuthorField.split("\\s*\\band\\b\\s*"); - if (tokens.length == 0) { - return ""; - } - StringBuilder author = new StringBuilder(); - author.append((tokens[0].split(","))[0]); - if (tokens.length == 2) { - author.append(delim).append((tokens[1].split(","))[0]); - } else if (tokens.length > 2) { - author.append(append); + private static String authEtal(AuthorList authorList, String delim, String append) { + if (authorList.getNumberOfAuthors() <= 2) { + return joinAuthorsOnLastName(authorList, 2, delim, ""); + } else { + return authorList.getAuthor(0).getLast().orElse("") + append; } - - return author.toString(); } /** - * The first N characters of the Mth author/editor. - * M starts counting from 1 + * The first N characters of the Mth author's or editor's last name. M starts counting from 1 */ - public static String authNofMth(String authorField, int n, int m) { + private static String authNofMth(AuthorList authorList, int n, int m) { // have m counting from 0 int mminusone = m - 1; - String fixedAuthorField = AuthorList.fixAuthorForAlphabetization(authorField); - - String[] tokens = fixedAuthorField.split("\\s+\\band\\b\\s+"); - if ((tokens.length <= mminusone) || (n < 0) || (mminusone < 0)) { + if ((authorList.getNumberOfAuthors() <= mminusone) || (n < 0) || (mminusone < 0)) { return ""; } - String lastName = (tokens[mminusone].split(","))[0]; - if (lastName.length() <= n) { - return lastName; - } else { - return lastName.substring(0, n); - } + + String lastName = authorList.getAuthor(mminusone).getLast() + .map(CitationKeyGenerator::removeDefaultUnwantedCharacters).orElse(""); + return lastName.length() > n ? lastName.substring(0, n) : lastName; } /** * First N chars of the first author's last name. */ - public static String authN(String authString, int num) { - String fa = firstAuthor(authString); - fa = CitationKeyGenerator.removeUnwantedCharacters(fa, DEFAULT_UNWANTED_CHARACTERS); - if (num > fa.length()) { - num = fa.length(); - } - return fa.substring(0, num); + private static String authN(AuthorList authorList, int num) { + return authNofMth(authorList, num, 1); } /** * authshort format: - * added by Kolja Brix, kbx@users.sourceforge.net - * + *

* given author names - * - * Isaac Newton and James Maxwell and Albert Einstein and N. Bohr - * - * Isaac Newton and James Maxwell and Albert Einstein - * - * Isaac Newton and James Maxwell - * - * Isaac Newton - * + *

  1. Isaac Newton and James Maxwell and Albert Einstein and N. Bohr
  2. + *
  3. Isaac Newton and James Maxwell and Albert Einstein
  4. + *
  5. Isaac Newton and James Maxwell
  6. + *
  7. Isaac Newton
* yield - * - * NME+ - * - * NME - * - * NM - * - * Newton + *
  1. NME+
  2. + *
  3. NME
  4. + *
  5. NM
  6. + *
  7. Newton

+ * {@author added by Kolja Brix, kbx@users.sourceforge.net} */ - public static String authshort(String authorField) { - String fixedAuthorField = AuthorList.fixAuthorForAlphabetization(authorField); + private static String authshort(AuthorList authorList) { StringBuilder author = new StringBuilder(); - String[] tokens = fixedAuthorField.split("\\band\\b"); - int i = 0; - - if (tokens.length == 1) { - author.append(authNofMth(fixedAuthorField, fixedAuthorField.length(), 1)); - } else if (tokens.length >= 2) { - while ((tokens.length > i) && (i < 3)) { - author.append(authNofMth(fixedAuthorField, 1, i + 1)); - i++; + final int numberOfAuthors = authorList.getNumberOfAuthors(); + + if (numberOfAuthors == 1) { + author.append(authorList.getAuthor(0).getLast().orElse("")); + } else if (numberOfAuthors >= 2) { + for (int i = 0; i < numberOfAuthors && i < 3; i++) { + author.append(authNofMth(authorList, 1, i + 1)); } - if (tokens.length > 3) { + if (numberOfAuthors > 3) { author.append('+'); } } @@ -953,75 +951,58 @@ public static String authshort(String authorField) { /** * authIniN format: - * - * Each author gets (N div #authors) chars, the remaining (N mod #authors) - * chars are equally distributed to the authors first in the row. - * - * If (N < #authors), only the first N authors get mentioned. - * + *

+ * Each author gets (N div #authors) chars, the remaining (N mod #authors) chars are equally distributed to the + * authors first in the row. If (N < #authors), only the first N authors get mentioned. + *

* For example if + *

    + *
  1. I. Newton and J. Maxwell and A. Einstein and N. Bohr (..)
  2. + *
  3. I. Newton and J. Maxwell and A. Einstein
  4. + *
  5. I. Newton and J. Maxwell
  6. + *
  7. I. Newton
  8. + *
+ * authIni4 gives: + *
    + *
  1. NMEB
  2. + *
  3. NeME
  4. + *
  5. NeMa
  6. + *
  7. Newt
  8. + *
* - * a) I. Newton and J. Maxwell and A. Einstein and N. Bohr (..) - * - * b) I. Newton and J. Maxwell and A. Einstein - * - * c) I. Newton and J. Maxwell - * - * d) I. Newton - * - * authIni4 gives: a) NMEB, b) NeME, c) NeMa, d) Newt - * - * @param authorField - * The authors to format. - * - * @param n - * The maximum number of characters this string will be long. A - * negative number or zero will lead to "" be returned. - * - * @throws NullPointerException - * if authorField is null and n > 0 + * @param authorList The authors to format. + * @param n The maximum number of characters this string will be long. A negative number or zero will lead + * to "" be returned. */ - public static String authIniN(String authorField, int n) { - - if (n <= 0) { + private static String authIniN(AuthorList authorList, int n) { + if (n <= 0 || authorList.isEmpty()) { return ""; } - String fixedAuthorField = AuthorList.fixAuthorForAlphabetization(authorField); StringBuilder author = new StringBuilder(); - String[] tokens = fixedAuthorField.split("\\band\\b"); + final int numberOfAuthors = authorList.getNumberOfAuthors(); - if (tokens.length == 0) { - return author.toString(); - } - - int i = 0; - int charsAll = n / tokens.length; - while (tokens.length > i) { - if (i < (n % tokens.length)) { - author.append(authNofMth(fixedAuthorField, charsAll + 1, i + 1)); + int charsAll = n / numberOfAuthors; + for (int i = 0; i < numberOfAuthors; i++) { + if (i < (n % numberOfAuthors)) { + author.append(authNofMth(authorList, charsAll + 1, i + 1)); } else { - author.append(authNofMth(fixedAuthorField, charsAll, i + 1)); + author.append(authNofMth(authorList, charsAll, i + 1)); } - i++; } if (author.length() <= n) { return author.toString(); } else { - return author.toString().substring(0, n); + return author.substring(0, n); } } /** * Split the pages field into separate numbers and return the lowest * - * @param pages - * (may not be null) a pages string such as 42--111 or - * 7,41,73--97 or 43+ - * + * @param pages (may not be null) a pages string such as 42--111 or 7,41,73--97 or 43+ * @return the first page number or "" if no number is found in the string - * * @throws NullPointerException if pages is null */ public static String firstPage(String pages) { @@ -1046,14 +1027,9 @@ public static String firstPage(String pages) { /** * Return the non-digit prefix of pages * - * @param pages - * a pages string such as L42--111 or L7,41,73--97 or L43+ - * - * @return the non-digit prefix of pages (like "L" of L7) - * or "" if no non-digit prefix is found in the string - * - * @throws NullPointerException - * if pages is null. + * @param pages a pages string such as L42--111 or L7,41,73--97 or L43+ + * @return the non-digit prefix of pages (like "L" of L7) or "" if no non-digit prefix is found in the string + * @throws NullPointerException if pages is null. */ public static String pagePrefix(String pages) { if (pages.matches("^\\D+.*$")) { @@ -1087,8 +1063,8 @@ public static String lastPage(String pages) { } /** - * Parse a field marker with modifiers, possibly containing a parenthesised modifier, - * as well as escaped colons and parentheses. + * Parse a field marker with modifiers, possibly containing a parenthesised modifier, as well as escaped colons and + * parentheses. * * @param arg The argument string. * @return An array of strings representing the parts of the marker @@ -1127,57 +1103,12 @@ protected static List parseFieldMarker(String arg) { return parts; } - private static String normalize(String content) { - List tokens = new ArrayList<>(); - int b = 0; - StringBuilder and = new StringBuilder(); - StringBuilder token = new StringBuilder(); - for (int p = 0; p < content.length(); p++) { - if (b == 0) { - String andString = and.toString(); // Avoid lots of calls - if (((andString.isEmpty()) && (content.charAt(p) == ' ')) - || (" ".equals(andString) && (content.charAt(p) == 'a')) - || (" a".equals(andString) && (content.charAt(p) == 'n')) - || (" an".equals(andString) && (content.charAt(p) == 'd'))) { - and.append(content.charAt(p)); - } else if (" and".equals(and.toString()) && (content.charAt(p) == ' ')) { - and = new StringBuilder(); - tokens.add(token.toString().trim()); - token = new StringBuilder(); - } else { - if (content.charAt(p) == '{') { - b++; - } - if (content.charAt(p) == '}') { - b--; - } - token.append(and); - and = new StringBuilder(); - token.append(content.charAt(p)); - } - } else { - token.append(content.charAt(p)); - } - } - tokens.add(token.toString()); - StringBuilder normalized = new StringBuilder(); - - for (int i = 0; i < tokens.size(); i++) { - if (i > 0) { - normalized.append(" and "); - } - - normalized.append(isInstitution(tokens.get(i)) ? generateInstitutionKey(tokens.get(i)) : removeDiacritics( - tokens.get(i))); - } - return normalized.toString(); - } - /** * Will remove diacritics from the content. - * - * Replaces umlaut: \"x with xe, e.g. \"o -> oe, \"u -> ue, etc. - * Removes all other diacritics: \?x -> x, e.g. \'a -> a, etc. + *
    + *
  • Replaces umlaut: \"x with xe, e.g. \"o -> oe, \"u -> ue, etc.
  • + *
  • Removes all other diacritics: \?x -> x, e.g. \'a -> a, etc.
  • + *
* * @param content The content. * @return The content without diacritics. @@ -1201,9 +1132,10 @@ private static String removeDiacritics(String content) { /** * Unifies umlauts. - * - * Replaces: $\ddot{\mathrm{X}}$ (an alternative umlaut) with: {\"X} - * Replaces: \?{X} and \?X with {\?X}, where ? is a diacritic symbol + *
    + *
  • Replaces: $\ddot{\mathrm{X}}$ (an alternative umlaut) with: {\"X}
  • + *
  • Replaces: \?{X} and \?X with {\?X}, where ? is a diacritic symbol
  • + *
* * @param content The content. * @return The content with unified diacritics. @@ -1216,73 +1148,44 @@ private static String unifyDiacritics(String content) { "{$1$2}"); } - /** - * Check if a value is institution. - * - * This is usable for distinguishing between persons and institutions in - * the author or editor fields. - * - * A person: - * - "John Doe" - * - "Doe, John" - * - * An institution: - * - "{The Big Company or Institution Inc.}" - * - "{The Big Company or Institution Inc. (BCI)}" - * - * @param author Author or editor. - * @return True if the author or editor is an institution. - */ - private static boolean isInstitution(String author) { - return StringUtil.isInCurlyBrackets(author); - } - /** *

- * An author or editor may be and institution not a person. In that case the - * key generator builds very long keys, e.g.: for “The Attributed - * Graph Grammar System (AGG)” -> - * “TheAttributedGraphGrammarSystemAGG”. + * An author or editor may be and institution not a person. In that case the key generator builds very long keys, + * e.g.: for “The Attributed Graph Grammar System (AGG)” -> “TheAttributedGraphGrammarSystemAGG”. *

* *

- * An institution name should be inside {} brackets. If the - * institution name includes its abbreviation this abbreviation should - * be in {} brackets. For the previous example the value - * should look like: + * An institution name should be inside {} brackets. If the institution name includes its abbreviation + * this abbreviation should be in {} brackets. For the previous example the value should look like: * {The Attributed Graph Grammar System ({AGG})}. *

* *

- * If an institution includes its abbreviation, i.e. "...({XYZ})", first - * such abbreviation should be used as the key value part of such author. + * If an institution includes its abbreviation, i.e. "...({XYZ})", first such abbreviation should be used as the key + * value part of such author. *

* *

- * If an institution does not include its abbreviation the key should be - * generated from its name in the following way: + * If an institution does not include its abbreviation the key should be generated from its name in the following + * way: *

* *

- * The institution value can contain: institution name, part of the - * institution, address, etc. These values should be comma separated. - * Institution name and possible part of the institution - * should be in the beginning, while address and secondary information - * should be in the end. + * The institution value can contain: institution name, part of the institution, address, etc. These values should + * be comma separated. Institution name and possible part of the institution should be in the beginning, while + * address and secondary information should be in the end. *

- * + *

* Each part is examined separately: *

    - *
  1. We remove all tokens of a part which are one of the defined ignore - * words (the, press), which end with a dot (ltd., co., ...) and which first - * character is lowercase (of, on, di, ...).
  2. + *
  3. We remove all tokens of a part which are one of the defined ignore words (the, press), which end with a dot + * (ltd., co., ...) and which first character is lowercase (of, on, di, ...).
  4. *
  5. We detect the types of the part: university, technology institute, * department, school, rest *
      *
    • University: "Uni[NameOfTheUniversity]"
    • - *
    • Department: If the institution value contains more than one comma - * separated part, the department will be an abbreviation of all words - * beginning with the uppercase letter except of words: + *
    • Department: If the institution value contains more than one comma separated part, the department will be an + * abbreviation of all words beginning with the uppercase letter except of words: * d[ei]p.*, school, faculty
    • *
    • School: same as department
    • *
    • Rest: If there are less than 3 tokens in such part than the result @@ -1363,11 +1266,11 @@ private static String generateInstitutionKey(String content) { for (String k : tokenParts) { if (noOtherInstitutionKeyWord(k)) { if (tokenTypes.contains(Institution.SCHOOL)) { - schoolSB.append(NOT_CAPITAL_FIRST_CHARACTER.matcher(k).replaceAll("")); + schoolSB.append(NOT_CAPITAL_CHARACTER.matcher(k).replaceAll("")); } // Explicitly defined department part is build the same way as school if (tokenTypes.contains(Institution.DEPARTMENT)) { - departmentSB.append(NOT_CAPITAL_FIRST_CHARACTER.matcher(k).replaceAll("")); + departmentSB.append(NOT_CAPITAL_CHARACTER.matcher(k).replaceAll("")); } } } @@ -1400,7 +1303,9 @@ private static String generateInstitutionKey(String content) { } /** - * Checks that this is not an institution keyword and has an uppercase first letter, except univ/tech key word. + * Helper method for {@link BracketedPattern#generateInstitutionKey(String)}. Checks that the word is not an + * institution keyword and has an uppercase first letter, except univ/tech key word. + * * @param word to check * @return */ @@ -1408,7 +1313,7 @@ private static boolean noOtherInstitutionKeyWord(String word) { return !DEPARTMENTS.matcher(word).matches() && !StandardField.SCHOOL.getName().equalsIgnoreCase(word) && !"faculty".equalsIgnoreCase(word) - && !NOT_CAPITAL_FIRST_CHARACTER.matcher(word).replaceAll("").isEmpty(); + && !NOT_CAPITAL_CHARACTER.matcher(word).replaceAll("").isEmpty(); } private static List getValidInstitutionNameParts(String name) { diff --git a/src/main/java/org/jabref/logic/citationkeypattern/CitationKeyGenerator.java b/src/main/java/org/jabref/logic/citationkeypattern/CitationKeyGenerator.java index e97ee6d17e0..5d13cfa7484 100644 --- a/src/main/java/org/jabref/logic/citationkeypattern/CitationKeyGenerator.java +++ b/src/main/java/org/jabref/logic/citationkeypattern/CitationKeyGenerator.java @@ -32,6 +32,7 @@ public class CitationKeyGenerator extends BracketedPattern { private final AbstractCitationKeyPattern citeKeyPattern; private final BibDatabase database; private final CitationKeyPatternPreferences citationKeyPatternPreferences; + private final String unwantedCharacters; public CitationKeyGenerator(BibDatabaseContext bibDatabaseContext, CitationKeyPatternPreferences citationKeyPatternPreferences) { this(bibDatabaseContext.getMetaData().getCiteKeyPattern(citationKeyPatternPreferences.getKeyPattern()), @@ -43,6 +44,7 @@ public CitationKeyGenerator(AbstractCitationKeyPattern citeKeyPattern, BibDataba this.citeKeyPattern = Objects.requireNonNull(citeKeyPattern); this.database = Objects.requireNonNull(database); this.citationKeyPatternPreferences = Objects.requireNonNull(citationKeyPatternPreferences); + this.unwantedCharacters = citationKeyPatternPreferences.getUnwantedCharacters(); } @Deprecated @@ -84,6 +86,10 @@ private static String getAppendix(int number) { } } + public static String removeDefaultUnwantedCharacters(String key) { + return removeUnwantedCharacters(key, DEFAULT_UNWANTED_CHARACTERS); + } + public static String removeUnwantedCharacters(String key, String unwantedCharacters) { String newKey = key.chars() .filter(c -> unwantedCharacters.indexOf(c) == -1) @@ -124,13 +130,13 @@ public String generateKey(BibEntry entry) { List parts = parseFieldMarker(typeListEntry); Character delimiter = citationKeyPatternPreferences.getKeywordDelimiter(); String pattern = "[" + parts.get(0) + "]"; - String label = expandBrackets(pattern, delimiter, entry, database); + String label = removeUnwantedCharacters(expandBrackets(pattern, delimiter, entry, database), unwantedCharacters); // apply modifier if present if (parts.size() > 1) { - label = applyModifiers(label, parts, 1); + label = removeUnwantedCharacters(applyModifiers(label, parts, 1), unwantedCharacters); } // Remove all illegal characters from the label. - label = cleanKey(label, citationKeyPatternPreferences.getUnwantedCharacters()); + label = cleanKey(label, unwantedCharacters); stringBuilder.append(label); } else { stringBuilder.append(typeListEntry); diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index b6ead0c3c2b..d73126b7312 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -298,7 +298,10 @@ private Optional genericGetResolvedFieldOrAlias(Field field, BibDatabase } } } - return result.map(resultText -> BibDatabase.getText(resultText, database)); + + return (database == null || result.isEmpty()) ? + result : + Optional.of(database.resolveForStrings(result.get())); } /** diff --git a/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java b/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java index e16f8221846..f5931a8f1b0 100644 --- a/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java +++ b/src/test/java/org/jabref/logic/citationkeypattern/BracketedPatternTest.java @@ -21,25 +21,23 @@ class BracketedPatternTest { @BeforeEach void setUp() { - bibentry = new BibEntry(); - bibentry.setField(StandardField.AUTHOR, "O. Kitsune"); - bibentry.setField(StandardField.YEAR, "2017"); - bibentry.setField(StandardField.PAGES, "213--216"); - - dbentry = new BibEntry(); - dbentry.setType(StandardEntryType.Article); - dbentry.setCitationKey("HipKro03"); - dbentry.setField(StandardField.AUTHOR, "Eric von Hippel and Georg von Krogh"); - dbentry.setField(StandardField.TITLE, "Open Source Software and the \"Private-Collective\" Innovation Model: Issues for Organization Science"); - dbentry.setField(StandardField.JOURNAL, "Organization Science"); - dbentry.setField(StandardField.YEAR, "2003"); - dbentry.setField(StandardField.VOLUME, "14"); - dbentry.setField(StandardField.PAGES, "209--223"); - dbentry.setField(StandardField.NUMBER, "2"); - dbentry.setField(StandardField.ADDRESS, "Institute for Operations Research and the Management Sciences (INFORMS), Linthicum, Maryland, USA"); - dbentry.setField(StandardField.DOI, "http://dx.doi.org/10.1287/orsc.14.2.209.14992"); - dbentry.setField(StandardField.ISSN, "1526-5455"); - dbentry.setField(StandardField.PUBLISHER, "INFORMS"); + bibentry = new BibEntry().withField(StandardField.AUTHOR, "O. Kitsune") + .withField(StandardField.YEAR, "2017") + .withField(StandardField.PAGES, "213--216"); + + dbentry = new BibEntry(StandardEntryType.Article) + .withCitationKey("HipKro03") + .withField(StandardField.AUTHOR, "Eric von Hippel and Georg von Krogh") + .withField(StandardField.TITLE, "Open Source Software and the \"Private-Collective\" Innovation Model: Issues for Organization Science") + .withField(StandardField.JOURNAL, "Organization Science") + .withField(StandardField.YEAR, "2003") + .withField(StandardField.VOLUME, "14") + .withField(StandardField.PAGES, "209--223") + .withField(StandardField.NUMBER, "2") + .withField(StandardField.ADDRESS, "Institute for Operations Research and the Management Sciences (INFORMS), Linthicum, Maryland, USA") + .withField(StandardField.DOI, "http://dx.doi.org/10.1287/orsc.14.2.209.14992") + .withField(StandardField.ISSN, "1526-5455") + .withField(StandardField.PUBLISHER, "INFORMS"); database = new BibDatabase(); database.insertEntry(dbentry); @@ -102,10 +100,10 @@ void databaseWithStringsExpansionTest() { BibDatabase another_database = new BibDatabase(); BibtexString string = new BibtexString("sgr", "Saulius Gražulis"); another_database.addString(string); - bibentry = new BibEntry(); - bibentry.setField(StandardField.AUTHOR, "#sgr#"); - bibentry.setField(StandardField.YEAR, "2017"); - bibentry.setField(StandardField.PAGES, "213--216"); + bibentry = new BibEntry() + .withField(StandardField.AUTHOR, "#sgr#") + .withField(StandardField.YEAR, "2017") + .withField(StandardField.PAGES, "213--216"); BracketedPattern pattern = new BracketedPattern("[year]_[auth]_[firstpage]"); assertEquals("2017_Gražulis_213", pattern.expand(bibentry, another_database)); @@ -139,10 +137,10 @@ void entryTypeExpansionLowercaseTest() { void suppliedBibentryBracketExpansionTest() { BibDatabase another_database = null; BracketedPattern pattern = new BracketedPattern("[year]_[auth]_[firstpage]"); - BibEntry another_bibentry = new BibEntry(); - another_bibentry.setField(StandardField.AUTHOR, "Gražulis, Saulius"); - another_bibentry.setField(StandardField.YEAR, "2017"); - another_bibentry.setField(StandardField.PAGES, "213--216"); + BibEntry another_bibentry = new BibEntry() + .withField(StandardField.AUTHOR, "Gražulis, Saulius") + .withField(StandardField.YEAR, "2017") + .withField(StandardField.PAGES, "213--216"); assertEquals("2017_Gražulis_213", pattern.expand(another_bibentry, ';', another_database)); } @@ -208,8 +206,7 @@ void lowerFormatterWorksOnVonNamePrefixes() { @Test void testResolvedFieldAndFormat() { - BibEntry child = new BibEntry(); - child.setField(StandardField.CROSSREF, "HipKro03"); + BibEntry child = new BibEntry().withField(StandardField.CROSSREF, "HipKro03"); database.insertEntry(child); Character separator = ';'; @@ -233,8 +230,8 @@ void testResolvedFieldAndFormat() { @Test void testResolvedParentNotInDatabase() { - BibEntry child = new BibEntry(); - child.setField(StandardField.CROSSREF, "HipKro03"); + BibEntry child = new BibEntry() + .withField(StandardField.CROSSREF, "HipKro03"); database.removeEntry(dbentry); database.insertEntry(child); @@ -273,4 +270,12 @@ void expandBracketsDoesNotTruncateWithoutAnArgumentToTruncateModifier() { assertEquals("Open Source Software and the \"Private-Collective\" Innovation Model: Issues for Organization Science", BracketedPattern.expandBrackets("[fulltitle:truncate]", ';', dbentry, database)); } + + @Test + void expandBracketsWithAuthorStartingWithBrackets() { + // Issue https://github.com/JabRef/jabref/issues/3920 + BibEntry bibEntry = new BibEntry() + .withField(StandardField.AUTHOR, "Patrik {\\v{S}}pan{\\v{e}}l and Kseniya Dryahina and David Smith"); + assertEquals("ŠpanělEtAl", BracketedPattern.expandBrackets("[authEtAl:latex_to_unicode]", null, bibEntry, null)); + } } diff --git a/src/test/java/org/jabref/logic/citationkeypattern/CitationKeyGeneratorTest.java b/src/test/java/org/jabref/logic/citationkeypattern/CitationKeyGeneratorTest.java index 642c270cf44..7305bc43f66 100644 --- a/src/test/java/org/jabref/logic/citationkeypattern/CitationKeyGeneratorTest.java +++ b/src/test/java/org/jabref/logic/citationkeypattern/CitationKeyGeneratorTest.java @@ -23,18 +23,22 @@ class CitationKeyGeneratorTest { + private static final BibEntry AUTHOR_EMPTY = createABibEntryAuthor(""); + private static final String AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1 = "Isaac Newton"; - private static final String AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2 = "Isaac Newton and James Maxwell"; + private static final BibEntry AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1 = createABibEntryAuthor("Isaac Newton"); + private static final BibEntry AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2 = createABibEntryAuthor("Isaac Newton and James Maxwell"); private static final String AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3 = "Isaac Newton and James Maxwell and Albert Einstein"; + private static final BibEntry AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3 = createABibEntryAuthor("Isaac Newton and James Maxwell and Albert Einstein"); - private static final String AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1 = "Wil van der Aalst"; - private static final String AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2 = "Wil van der Aalst and Tammo van Lessen"; + private static final BibEntry AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1 = createABibEntryAuthor("Wil van der Aalst"); + private static final BibEntry AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2 = createABibEntryAuthor("Wil van der Aalst and Tammo van Lessen"); - private static final String AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1 = "I. Newton"; - private static final String AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2 = "I. Newton and J. Maxwell"; - private static final String AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3 = "I. Newton and J. Maxwell and A. Einstein"; - private static final String AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4 = "I. Newton and J. Maxwell and A. Einstein and N. Bohr"; - private static final String AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5 = "I. Newton and J. Maxwell and A. Einstein and N. Bohr and Harry Unknown"; + private static final BibEntry AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1 = createABibEntryAuthor("I. Newton"); + private static final BibEntry AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2 = createABibEntryAuthor("I. Newton and J. Maxwell"); + private static final BibEntry AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3 = createABibEntryAuthor("I. Newton and J. Maxwell and A. Einstein"); + private static final BibEntry AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4 = createABibEntryAuthor("I. Newton and J. Maxwell and A. Einstein and N. Bohr"); + private static final BibEntry AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5 = createABibEntryAuthor("I. Newton and J. Maxwell and A. Einstein and N. Bohr and Harry Unknown"); private static final String TITLE_STRING_ALL_LOWER_FOUR_SMALL_WORDS_ONE_EN_DASH = "application migration effort in the cloud - the case of cloud platforms"; private static final String TITLE_STRING_ALL_LOWER_FIRST_WORD_IN_BRACKETS_TWO_SMALL_WORDS_SMALL_WORD_AFTER_COLON = "{BPEL} conformance in open source engines: the case of static analysis"; @@ -45,6 +49,20 @@ class CitationKeyGeneratorTest { private static final String TITLE_STRING_CASED_TWO_SMALL_WORDS_ONE_CONNECTED_WORD = "Towards Choreography-based Process Distribution in the Cloud"; private static final String TITLE_STRING_CASED_FOUR_SMALL_WORDS_TWO_CONNECTED_WORDS = "On the Measurement of Design-Time Adaptability for Process-Based Systems "; + private static final String AUTHSHORT = "[authshort]"; + private static final String AUTHNOFMTH = "[auth%d_%d]"; + private static final String AUTHFOREINI = "[authForeIni]"; + private static final String AUTHFIRSTFULL = "[authFirstFull]"; + private static final String AUTHORS = "[authors]"; + private static final String AUTHORSALPHA = "[authorsAlpha]"; + private static final String AUTHORLAST = "[authorLast]"; + private static final String AUTHORLASTFOREINI = "[authorLastForeIni]"; + private static final String AUTHORINI = "[authorIni]"; + private static final String AUTHORN = "[authors%d]"; + private static final String AUTHETAL = "[authEtAl]"; + private static final String AUTH_ETAL = "[auth.etal]"; + private static final String AUTHAUTHEA = "[auth.auth.ea]"; + private static ImportFormatPreferences importFormatPreferences; private final FileUpdateMonitor fileMonitor = new DummyFileUpdateMonitor(); @@ -53,13 +71,17 @@ void setUp() { importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); } + private static BibEntry createABibEntryAuthor(String author) { + return new BibEntry().withField(StandardField.AUTHOR, author); + } + static String generateKey(BibEntry entry, String pattern) { return generateKey(entry, pattern, new BibDatabase()); } static String generateKey(BibEntry entry, String pattern, BibDatabase database) { GlobalCitationKeyPattern keyPattern = new GlobalCitationKeyPattern(Collections.emptyList()); - keyPattern.setDefaultValue("[" + pattern + "]"); + keyPattern.setDefaultValue(pattern); CitationKeyPatternPreferences patternPreferences = new CitationKeyPatternPreferences( false, false, @@ -79,23 +101,22 @@ void testAndInAuthorName() throws ParseException { Optional entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Simon Holland}}", importFormatPreferences, fileMonitor); assertEquals("Holland", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @Test void testCrossrefAndInAuthorNames() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - BibEntry entry2 = new BibEntry(); - entry2.setCitationKey("entry2"); - entry2.setField(StandardField.AUTHOR, "Simon Holland"); + BibEntry entry1 = new BibEntry().withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.AUTHOR, "Simon Holland"); database.insertEntry(entry1); database.insertEntry(entry2); assertEquals("Holland", - CitationKeyGenerator.cleanKey(generateKey(entry1, "auth", + CitationKeyGenerator.cleanKey(generateKey(entry1, "[auth]", database), DEFAULT_UNWANTED_CHARACTERS)); } @@ -104,23 +125,23 @@ void testAndAuthorNames() throws ParseException { String bibtexString = "@ARTICLE{whatevery, author={Mari D. Herland and Mona-Iren Hauge and Ingeborg M. Helgeland}}"; Optional entry = BibtexParser.singleFromString(bibtexString, importFormatPreferences, fileMonitor); assertEquals("HerlandHaugeHelgeland", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "authors3", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[authors3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @Test void testCrossrefAndAuthorNames() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - BibEntry entry2 = new BibEntry(); - entry2.setCitationKey("entry2"); - entry2.setField(StandardField.AUTHOR, "Mari D. Herland and Mona-Iren Hauge and Ingeborg M. Helgeland"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.AUTHOR, "Mari D. Herland and Mona-Iren Hauge and Ingeborg M. Helgeland"); database.insertEntry(entry1); database.insertEntry(entry2); assertEquals("HerlandHaugeHelgeland", - CitationKeyGenerator.cleanKey(generateKey(entry1, "authors3", + CitationKeyGenerator.cleanKey(generateKey(entry1, "[authors3]", database), DEFAULT_UNWANTED_CHARACTERS)); } @@ -129,7 +150,7 @@ void testSpecialLatexCharacterInAuthorName() throws ParseException { Optional entry = BibtexParser.singleFromString( "@ARTICLE{kohn, author={Simon Popovi\\v{c}ov\\'{a}}}", importFormatPreferences, fileMonitor); assertEquals("Popovicova", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @@ -143,73 +164,73 @@ void testMakeLabelAndCheckLegalKeys() throws ParseException { Optional entry0 = BibtexParser.singleFromString( "@ARTICLE{kohn, author={Andreas Köning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Koe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Áöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Aoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Éöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Eoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Íöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Ioe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Ĺöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Loe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Ńöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Noe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Óöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Ooe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Ŕöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Roe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Śöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Soe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Úöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Uoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Ýöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Yoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Źöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Zoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @@ -221,55 +242,56 @@ void testMakeLabelAndCheckLegalKeysAccentGrave() throws ParseException { Optional entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Àöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Aoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Èöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Eoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Ìöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Ioe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Òöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Ooe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andreas Ùöning}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Uoe", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Oraib Al-Ketan}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("AlK", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andrés D'Alessandro}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("DAl", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); entry0 = BibtexParser.singleFromString("@ARTICLE{kohn, author={Andrés Aʹrnold}, year={2000}}", importFormatPreferences, fileMonitor); assertEquals("Arn", - CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "auth3", + CitationKeyGenerator.cleanKey(generateKey(entry0.orElse(null), "[auth3]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } /** - * Tests if cleanKey replaces Non-ASCII chars. There are quite a few chars that should be replaced. Perhaps - * there is a better method than the current. + * Tests if cleanKey replaces Non-ASCII chars. There are quite a few chars that should be replaced. Perhaps there is + * a better method than the current. + * * @see CitationKeyGenerator#cleanKey(String, String) */ @Test @@ -332,18 +354,13 @@ void testCheckLegalKey() { @Test void testFirstAuthor() { - assertEquals("Newton", CitationKeyGenerator.firstAuthor(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5)); - assertEquals("Newton", CitationKeyGenerator.firstAuthor(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, "[auth]")); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, "[auth]")); // https://sourceforge.net/forum/message.php?msg_id=4498555 - assertEquals("K{\\\"o}ning", CitationKeyGenerator.firstAuthor("K{\\\"o}ning")); + assertEquals("Koening", generateKey(createABibEntryAuthor("K{\\\"o}ning"), "[auth]")); - assertEquals("", CitationKeyGenerator.firstAuthor("")); - } - - @Test - void testFirstAuthorNull() { - assertThrows(NullPointerException.class, () -> CitationKeyGenerator.firstAuthor(null)); + assertEquals("", generateKey(createABibEntryAuthor(""), "[auth]")); } @Test @@ -351,23 +368,23 @@ void testUniversity() throws ParseException { Optional entry = BibtexParser.singleFromString( "@ARTICLE{kohn, author={{Link{\\\"{o}}ping University}}}", importFormatPreferences, fileMonitor); assertEquals("UniLinkoeping", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @Test void testcrossrefUniversity() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - BibEntry entry2 = new BibEntry(); - entry2.setCitationKey("entry2"); - entry2.setField(StandardField.AUTHOR, "{Link{\\\"{o}}ping University}}"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.AUTHOR, "{Link{\\\"{o}}ping University}}"); database.insertEntry(entry1); database.insertEntry(entry2); assertEquals("UniLinkoeping", - CitationKeyGenerator.cleanKey(generateKey(entry1, "auth", + CitationKeyGenerator.cleanKey(generateKey(entry1, "[auth]", database), DEFAULT_UNWANTED_CHARACTERS)); } @@ -377,23 +394,23 @@ void testDepartment() throws ParseException { "@ARTICLE{kohn, author={{Link{\\\"{o}}ping University, Department of Electrical Engineering}}}", importFormatPreferences, fileMonitor); assertEquals("UniLinkoepingEE", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @Test void testcrossrefDepartment() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - BibEntry entry2 = new BibEntry(); - entry2.setCitationKey("entry2"); - entry2.setField(StandardField.AUTHOR, "{Link{\\\"{o}}ping University, Department of Electrical Engineering}}"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.AUTHOR, "{Link{\\\"{o}}ping University, Department of Electrical Engineering}}"); database.insertEntry(entry1); database.insertEntry(entry2); assertEquals("UniLinkoepingEE", - CitationKeyGenerator.cleanKey(generateKey(entry1, "auth", + CitationKeyGenerator.cleanKey(generateKey(entry1, "[auth]", database), DEFAULT_UNWANTED_CHARACTERS)); } @@ -403,7 +420,7 @@ void testSchool() throws ParseException { "@ARTICLE{kohn, author={{Link{\\\"{o}}ping University, School of Computer Engineering}}}", importFormatPreferences, fileMonitor); assertEquals("UniLinkoepingCE", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @@ -413,7 +430,7 @@ void generateKeyAbbreviateCorporateAuthorDepartmentWithoutAcademicInstitute() th "@ARTICLE{null, author={{Department of Localhost NullGenerators}}}", importFormatPreferences, fileMonitor); assertEquals("DLN", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @@ -423,23 +440,23 @@ void generateKeyAbbreviateCorporateAuthorSchoolWithoutAcademicInstitute() throws "@ARTICLE{null, author={{The School of Null}}}", importFormatPreferences, fileMonitor); assertEquals("SchoolNull", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @Test void testcrossrefSchool() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - BibEntry entry2 = new BibEntry(); - entry2.setCitationKey("entry2"); - entry2.setField(StandardField.AUTHOR, "{Link{\\\"{o}}ping University, School of Computer Engineering}}"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.AUTHOR, "{Link{\\\"{o}}ping University, School of Computer Engineering}}"); database.insertEntry(entry1); database.insertEntry(entry2); assertEquals("UniLinkoepingCE", - CitationKeyGenerator.cleanKey(generateKey(entry1, "auth", + CitationKeyGenerator.cleanKey(generateKey(entry1, "[auth]", database), DEFAULT_UNWANTED_CHARACTERS)); } @@ -448,51 +465,45 @@ void testInstituteOfTechnology() throws ParseException { Optional entry = BibtexParser.singleFromString( "@ARTICLE{kohn, author={{Massachusetts Institute of Technology}}}", importFormatPreferences, fileMonitor); assertEquals("MIT", - CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "auth", + CitationKeyGenerator.cleanKey(generateKey(entry.orElse(null), "[auth]", new BibDatabase()), DEFAULT_UNWANTED_CHARACTERS)); } @Test void testcrossrefInstituteOfTechnology() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - BibEntry entry2 = new BibEntry(); - entry2.setCitationKey("entry2"); - entry2.setField(StandardField.AUTHOR, "{Massachusetts Institute of Technology}"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.AUTHOR, "{Massachusetts Institute of Technology}"); database.insertEntry(entry1); database.insertEntry(entry2); assertEquals("MIT", - CitationKeyGenerator.cleanKey(generateKey(entry1, "auth", + CitationKeyGenerator.cleanKey(generateKey(entry1, "[auth]", database), DEFAULT_UNWANTED_CHARACTERS)); } @Test void testAuthIniN() { - assertEquals("NMEB", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, 4)); - assertEquals("NMEB", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, 4)); - assertEquals("NeME", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, 4)); - assertEquals("NeMa", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, 4)); - assertEquals("Newt", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 4)); - assertEquals("", ""); - - assertEquals("N", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 1)); - assertEquals("", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 0)); - assertEquals("", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, -1)); + assertEquals("NMEB", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, "[authIni4]")); + assertEquals("NMEB", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, "[authIni4]")); + assertEquals("NeME", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, "[authIni4]")); + assertEquals("NeMa", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, "[authIni4]")); + assertEquals("Newt", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, "[authIni4]")); + assertEquals("", generateKey(AUTHOR_EMPTY, "[authIni4]")); - assertEquals("Newton", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 6)); - assertEquals("Newton", CitationKeyGenerator.authIniN(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 7)); - } + assertEquals("N", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, "[authIni1]")); + assertEquals("", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, "[authIni0]")); - @Test - void testAuthIniNNull() { - assertThrows(NullPointerException.class, () -> CitationKeyGenerator.authIniN(null, 3)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, "[authIni6]")); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, "[authIni7]")); } @Test void testAuthIniNEmptyReturnsEmpty() { - assertEquals("", CitationKeyGenerator.authIniN("", 1)); + assertEquals("", generateKey(AUTHOR_EMPTY, "[authIni1]")); } /** @@ -500,16 +511,14 @@ void testAuthIniNEmptyReturnsEmpty() { */ @Test void authAuthEa() { - assertEquals("Newton", CitationKeyGenerator.authAuthEa(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1)); - assertEquals("Newton.Maxwell", - CitationKeyGenerator.authAuthEa(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2)); - assertEquals("Newton.Maxwell.ea", - CitationKeyGenerator.authAuthEa(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1, AUTHAUTHEA)); + assertEquals("Newton.Maxwell", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2, AUTHAUTHEA)); + assertEquals("Newton.Maxwell.ea", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3, AUTHAUTHEA)); } @Test void testAuthEaEmptyReturnsEmpty() { - assertEquals("", CitationKeyGenerator.authAuthEa("")); + assertEquals("", generateKey(AUTHOR_EMPTY, AUTHAUTHEA)); } /** @@ -520,20 +529,12 @@ void testAuthEtAl() { // tests taken from the comments // [auth.etal] - String delim = "."; - String append = ".etal"; - assertEquals("Newton.etal", - CitationKeyGenerator.authEtal(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3, delim, append)); - assertEquals("Newton.Maxwell", - CitationKeyGenerator.authEtal(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2, delim, append)); + assertEquals("Newton.etal", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3, AUTH_ETAL)); + assertEquals("Newton.Maxwell", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2, AUTH_ETAL)); // [authEtAl] - delim = ""; - append = "EtAl"; - assertEquals("NewtonEtAl", - CitationKeyGenerator.authEtal(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3, delim, append)); - assertEquals("NewtonMaxwell", - CitationKeyGenerator.authEtal(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2, delim, append)); + assertEquals("NewtonEtAl", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3, AUTHETAL)); + assertEquals("NewtonMaxwell", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2, AUTHETAL)); } /** @@ -542,15 +543,15 @@ void testAuthEtAl() { @Test void testAuthShort() { // tests taken from the comments - assertEquals("NME+", CitationKeyGenerator.authshort(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4)); - assertEquals("NME", CitationKeyGenerator.authshort(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3)); - assertEquals("NM", CitationKeyGenerator.authshort(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2)); - assertEquals("Newton", CitationKeyGenerator.authshort(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); + assertEquals("NME", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, AUTHSHORT)); + assertEquals("NME", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, AUTHSHORT)); + assertEquals("NM", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHSHORT)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHSHORT)); } @Test void testAuthShortEmptyReturnsEmpty() { - assertEquals("", CitationKeyGenerator.authshort("")); + assertEquals("", generateKey(AUTHOR_EMPTY, AUTHSHORT)); } /** @@ -558,27 +559,16 @@ void testAuthShortEmptyReturnsEmpty() { */ @Test void authNM() { - assertEquals("N", CitationKeyGenerator.authNofMth(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 1, 1)); - assertEquals("Max", - CitationKeyGenerator.authNofMth(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, 3, 2)); - assertEquals("New", - CitationKeyGenerator.authNofMth(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, 3, 1)); - assertEquals("Bo", - CitationKeyGenerator.authNofMth(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, 2, 4)); - assertEquals("Bohr", - CitationKeyGenerator.authNofMth(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, 6, 4)); - - assertEquals("Aal", - CitationKeyGenerator.authNofMth(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, 3, 1)); - assertEquals("Less", - CitationKeyGenerator.authNofMth(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, 4, 2)); + assertEquals("N", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, String.format(AUTHNOFMTH, 1, 1))); + assertEquals("Max", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, String.format(AUTHNOFMTH, 3, 2))); + assertEquals("New", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, String.format(AUTHNOFMTH, 3, 1))); + assertEquals("Bo", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, String.format(AUTHNOFMTH, 2, 4))); + assertEquals("Bohr", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, String.format(AUTHNOFMTH, 6, 4))); - assertEquals("", CitationKeyGenerator.authNofMth("", 2, 4)); - } + assertEquals("Aal", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, String.format(AUTHNOFMTH, 3, 1))); + assertEquals("Less", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, String.format(AUTHNOFMTH, 4, 2))); - @Test - void authNMThrowsNPE() { - assertThrows(NullPointerException.class, () -> CitationKeyGenerator.authNofMth(null, 2, 4)); + assertEquals("", generateKey(AUTHOR_EMPTY, String.format(AUTHNOFMTH, 2, 4))); } /** @@ -586,14 +576,10 @@ void authNMThrowsNPE() { */ @Test void firstAuthorForenameInitials() { - assertEquals("I", CitationKeyGenerator - .firstAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); - assertEquals("I", CitationKeyGenerator - .firstAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2)); - assertEquals("I", - CitationKeyGenerator.firstAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1)); - assertEquals("I", - CitationKeyGenerator.firstAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2)); + assertEquals("I", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHFOREINI)); + assertEquals("I", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHFOREINI)); + assertEquals("I", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1, AUTHFOREINI)); + assertEquals("I", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2, AUTHFOREINI)); } /** @@ -601,18 +587,14 @@ void firstAuthorForenameInitials() { */ @Test void firstAuthorVonAndLast() { - assertEquals("vanderAalst", CitationKeyGenerator - .firstAuthorVonAndLast(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1)); - assertEquals("vanderAalst", CitationKeyGenerator - .firstAuthorVonAndLast(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2)); + assertEquals("vanderAalst", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, AUTHFIRSTFULL)); + assertEquals("vanderAalst", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, AUTHFIRSTFULL)); } @Test void firstAuthorVonAndLastNoVonInName() { - assertEquals("Newton", - CitationKeyGenerator.firstAuthorVonAndLast(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1)); - assertEquals("Newton", - CitationKeyGenerator.firstAuthorVonAndLast(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1, AUTHFIRSTFULL)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_2, AUTHFIRSTFULL)); } /** @@ -620,11 +602,9 @@ void firstAuthorVonAndLastNoVonInName() { */ @Test void testAllAuthors() { - assertEquals("Newton", CitationKeyGenerator.allAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); - assertEquals("NewtonMaxwell", - CitationKeyGenerator.allAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2)); - assertEquals("NewtonMaxwellEinstein", - CitationKeyGenerator.allAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHORS)); + assertEquals("NewtonMaxwell", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHORS)); + assertEquals("NewtonMaxwellEinstein", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, AUTHORS)); } /** @@ -632,16 +612,14 @@ void testAllAuthors() { */ @Test void authorsAlpha() { - assertEquals("New", CitationKeyGenerator.authorsAlpha(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); - assertEquals("NM", CitationKeyGenerator.authorsAlpha(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2)); - assertEquals("NME", CitationKeyGenerator.authorsAlpha(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3)); - assertEquals("NMEB", CitationKeyGenerator.authorsAlpha(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4)); - assertEquals("NME+", CitationKeyGenerator.authorsAlpha(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5)); + assertEquals("New", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHORSALPHA)); + assertEquals("NM", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHORSALPHA)); + assertEquals("NME", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, AUTHORSALPHA)); + assertEquals("NMEB", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, AUTHORSALPHA)); + assertEquals("NME", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, AUTHORSALPHA)); - assertEquals("vdAal", - CitationKeyGenerator.authorsAlpha(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1)); - assertEquals("vdAvL", - CitationKeyGenerator.authorsAlpha(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2)); + assertEquals("vdAal", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, AUTHORSALPHA)); + assertEquals("vdAvL", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, AUTHORSALPHA)); } /** @@ -649,17 +627,14 @@ void authorsAlpha() { */ @Test void lastAuthor() { - assertEquals("Newton", CitationKeyGenerator.lastAuthor(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); - assertEquals("Maxwell", CitationKeyGenerator.lastAuthor(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2)); - assertEquals("Einstein", - CitationKeyGenerator.lastAuthor(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3)); - assertEquals("Bohr", CitationKeyGenerator.lastAuthor(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4)); - assertEquals("Unknown", CitationKeyGenerator.lastAuthor(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHORLAST)); + assertEquals("Maxwell", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHORLAST)); + assertEquals("Einstein", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, AUTHORLAST)); + assertEquals("Bohr", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, AUTHORLAST)); + assertEquals("Unknown", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, AUTHORLAST)); - assertEquals("Aalst", - CitationKeyGenerator.lastAuthor(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1)); - assertEquals("Lessen", - CitationKeyGenerator.lastAuthor(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2)); + assertEquals("Aalst", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, AUTHORLAST)); + assertEquals("Lessen", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, AUTHORLAST)); } /** @@ -667,21 +642,14 @@ void lastAuthor() { */ @Test void lastAuthorForenameInitials() { - assertEquals("I", - CitationKeyGenerator.lastAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); - assertEquals("J", - CitationKeyGenerator.lastAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2)); - assertEquals("A", - CitationKeyGenerator.lastAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3)); - assertEquals("N", - CitationKeyGenerator.lastAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4)); - assertEquals("H", - CitationKeyGenerator.lastAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5)); - - assertEquals("W", CitationKeyGenerator - .lastAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1)); - assertEquals("T", CitationKeyGenerator - .lastAuthorForenameInitials(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2)); + assertEquals("I", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHORLASTFOREINI)); + assertEquals("J", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHORLASTFOREINI)); + assertEquals("A", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, AUTHORLASTFOREINI)); + assertEquals("N", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, AUTHORLASTFOREINI)); + assertEquals("H", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, AUTHORLASTFOREINI)); + + assertEquals("W", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, AUTHORLASTFOREINI)); + assertEquals("T", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, AUTHORLASTFOREINI)); } /** @@ -689,21 +657,14 @@ void lastAuthorForenameInitials() { */ @Test void oneAuthorPlusIni() { - assertEquals("Newto", - CitationKeyGenerator.oneAuthorPlusIni(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1)); - assertEquals("NewtoM", - CitationKeyGenerator.oneAuthorPlusIni(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2)); - assertEquals("NewtoME", - CitationKeyGenerator.oneAuthorPlusIni(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3)); - assertEquals("NewtoMEB", - CitationKeyGenerator.oneAuthorPlusIni(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4)); - assertEquals("NewtoMEBU", - CitationKeyGenerator.oneAuthorPlusIni(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5)); - - assertEquals("Aalst", - CitationKeyGenerator.oneAuthorPlusIni(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1)); - assertEquals("AalstL", - CitationKeyGenerator.oneAuthorPlusIni(AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2)); + assertEquals("Newto", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, AUTHORINI)); + assertEquals("NewtoM", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, AUTHORINI)); + assertEquals("NewtoME", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, AUTHORINI)); + assertEquals("NewtoMEB", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, AUTHORINI)); + assertEquals("NewtoMEBU", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_5, AUTHORINI)); + + assertEquals("Aalst", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_1, AUTHORINI)); + assertEquals("AalstL", generateKey(AUTHOR_FIRSTNAME_FULL_LASTNAME_FULL_WITH_VAN_COUNT_2, AUTHORINI)); } /** @@ -711,18 +672,15 @@ void oneAuthorPlusIni() { */ @Test void testNAuthors1() { - assertEquals("Newton", CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 1)); - assertEquals("NewtonEtAl", - CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, 1)); - assertEquals("NewtonEtAl", - CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, 1)); - assertEquals("NewtonEtAl", - CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, 1)); + assertEquals("Newton", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, String.format(AUTHORN, 1))); + assertEquals("NewtonEtAl", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, String.format(AUTHORN, 1))); + assertEquals("NewtonEtAl", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, String.format(AUTHORN, 1))); + assertEquals("NewtonEtAl", generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, String.format(AUTHORN, 1))); } @Test void testNAuthors1EmptyReturnEmpty() { - assertEquals("", CitationKeyGenerator.nAuthors("", 1)); + assertEquals("", generateKey(AUTHOR_EMPTY, String.format(AUTHORN, 1))); } /** @@ -730,13 +688,14 @@ void testNAuthors1EmptyReturnEmpty() { */ @Test void testNAuthors3() { - assertEquals("Newton", CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, 3)); + assertEquals("Newton", + generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_1, String.format(AUTHORN, 3))); assertEquals("NewtonMaxwell", - CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, 3)); + generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_2, String.format(AUTHORN, 3))); assertEquals("NewtonMaxwellEinstein", - CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, 3)); + generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_3, String.format(AUTHORN, 3))); assertEquals("NewtonMaxwellEinsteinEtAl", - CitationKeyGenerator.nAuthors(AUTHOR_STRING_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, 3)); + generateKey(AUTHOR_FIRSTNAME_INITIAL_LASTNAME_FULL_COUNT_4, String.format(AUTHORN, 3))); } @Test @@ -921,59 +880,57 @@ void title() { @Test void keywordNKeywordsSeparatedBySpace() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.KEYWORDS, "w1, w2a w2b, w3"); + BibEntry entry = new BibEntry().withField(StandardField.KEYWORDS, "w1, w2a w2b, w3"); - assertEquals("w1", generateKey(entry, "keyword1")); + assertEquals("w1", generateKey(entry, "[keyword1]")); // check keywords with space - assertEquals("w2aw2b", generateKey(entry, "keyword2")); + assertEquals("w2aw2b", generateKey(entry, "[keyword2]")); // check out of range - assertEquals("", generateKey(entry, "keyword4")); + assertEquals("", generateKey(entry, "[keyword4]")); } @Test void crossrefkeywordNKeywordsSeparatedBySpace() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - BibEntry entry2 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - entry2.setCitationKey("entry2"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2"); database.insertEntry(entry2); database.insertEntry(entry1); entry2.setField(StandardField.KEYWORDS, "w1, w2a w2b, w3"); - assertEquals("w1", generateKey(entry1, "keyword1", database)); + assertEquals("w1", generateKey(entry1, "[keyword1]", database)); } @Test void keywordsNKeywordsSeparatedBySpace() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.KEYWORDS, "w1, w2a w2b, w3"); + BibEntry entry = new BibEntry().withField(StandardField.KEYWORDS, "w1, w2a w2b, w3"); // all keywords - assertEquals("w1w2aw2bw3", generateKey(entry, "keywords")); + assertEquals("w1w2aw2bw3", generateKey(entry, "[keywords]")); // check keywords with space - assertEquals("w1w2aw2b", generateKey(entry, "keywords2")); + assertEquals("w1w2aw2b", generateKey(entry, "[keywords2]")); // check out of range - assertEquals("w1w2aw2bw3", generateKey(entry, "keywords55")); + assertEquals("w1w2aw2bw3", generateKey(entry, "[keywords55]")); } @Test void crossrefkeywordsNKeywordsSeparatedBySpace() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - BibEntry entry2 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - entry2.setCitationKey("entry2"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.KEYWORDS, "w1, w2a w2b, w3"); database.insertEntry(entry2); database.insertEntry(entry1); - entry2.setField(StandardField.KEYWORDS, "w1, w2a w2b, w3"); - assertEquals("w1w2aw2bw3", generateKey(entry1, "keywords", database)); + assertEquals("w1w2aw2bw3", generateKey(entry1, "[keywords]", database)); } @Test @@ -998,138 +955,143 @@ void testCheckLegalNullInNullOut() { @Test void testApplyModifiers() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "Green Scheduling of Whatever"); - assertEquals("GSo", generateKey(entry, "shorttitleINI")); - assertEquals("GreenSchedulingWhatever", generateKey(entry, "shorttitle", + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "Green Scheduling of Whatever"); + assertEquals("GSo", generateKey(entry, "[shorttitleINI]")); + assertEquals("GreenSchedulingWhatever", generateKey(entry, "[shorttitle]", new BibDatabase())); } @Test void testcrossrefShorttitle() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - BibEntry entry2 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - entry2.setCitationKey("entry2"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.TITLE, "Green Scheduling of Whatever"); database.insertEntry(entry2); database.insertEntry(entry1); - entry2.setField(StandardField.TITLE, "Green Scheduling of Whatever"); - assertEquals("GreenSchedulingWhatever", generateKey(entry1, "shorttitle", + assertEquals("GreenSchedulingWhatever", generateKey(entry1, "[shorttitle]", database)); } @Test void testcrossrefShorttitleInitials() { BibDatabase database = new BibDatabase(); - BibEntry entry1 = new BibEntry(); - BibEntry entry2 = new BibEntry(); - entry1.setField(StandardField.CROSSREF, "entry2"); - entry2.setCitationKey("entry2"); + BibEntry entry1 = new BibEntry() + .withField(StandardField.CROSSREF, "entry2"); + BibEntry entry2 = new BibEntry() + .withCitationKey("entry2") + .withField(StandardField.TITLE, "Green Scheduling of Whatever"); database.insertEntry(entry2); database.insertEntry(entry1); - entry2.setField(StandardField.TITLE, "Green Scheduling of Whatever"); - assertEquals("GSo", generateKey(entry1, "shorttitleINI", database)); + assertEquals("GSo", generateKey(entry1, "[shorttitleINI]", database)); } @Test void generateKeyStripsColonFromTitle() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "Green Scheduling of: Whatever"); - assertEquals("GreenSchedulingOfWhatever", generateKey(entry, "title")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "Green Scheduling of: Whatever"); + assertEquals("GreenSchedulingOfWhatever", generateKey(entry, "[title]")); } @Test void generateKeyStripsApostropheFromTitle() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "Green Scheduling of `Whatever`"); - assertEquals("GreenSchedulingofWhatever", generateKey(entry, "title")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "Green Scheduling of `Whatever`"); + assertEquals("GreenSchedulingofWhatever", generateKey(entry, "[title]")); } @Test void generateKeyWithOneModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "The Interesting Title"); - assertEquals("theinterestingtitle", generateKey(entry, "title:lower")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "The Interesting Title"); + assertEquals("theinterestingtitle", generateKey(entry, "[title:lower]")); } @Test void generateKeyWithTwoModifiers() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "The Interesting Title"); - assertEquals("theinterestingtitle", generateKey(entry, "title:lower:(_)")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "The Interesting Title"); + assertEquals("theinterestingtitle", generateKey(entry, "[title:lower:(_)]")); } @Test void generateKeyWithTitleCapitalizeModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "the InTeresting title longer than THREE words"); - assertEquals("TheInterestingTitleLongerThanThreeWords", generateKey(entry, "title:capitalize")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "the InTeresting title longer than THREE words"); + assertEquals("TheInterestingTitleLongerThanThreeWords", generateKey(entry, "[title:capitalize]")); } @Test void generateKeyWithShortTitleCapitalizeModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "the InTeresting title longer than THREE words"); - assertEquals("InterestingTitleLonger", generateKey(entry, "shorttitle:capitalize")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "the InTeresting title longer than THREE words"); + assertEquals("InterestingTitleLonger", generateKey(entry, "[shorttitle:capitalize]")); } @Test void generateKeyWithTitleTitleCaseModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "A title WITH some of The key words"); - assertEquals("ATitlewithSomeoftheKeyWords", generateKey(entry, "title:titlecase")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "A title WITH some of The key words"); + assertEquals("ATitlewithSomeoftheKeyWords", generateKey(entry, "[title:titlecase]")); } @Test void generateKeyWithShortTitleTitleCaseModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "the InTeresting title longer than THREE words"); - assertEquals("InterestingTitleLonger", generateKey(entry, "shorttitle:titlecase")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "the InTeresting title longer than THREE words"); + assertEquals("InterestingTitleLonger", generateKey(entry, "[shorttitle:titlecase]")); } @Test void generateKeyWithTitleSentenceCaseModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.TITLE, "A title WITH some of The key words"); - assertEquals("Atitlewithsomeofthekeywords", generateKey(entry, "title:sentencecase")); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "A title WITH some of The key words"); + assertEquals("Atitlewithsomeofthekeywords", generateKey(entry, "[title:sentencecase]")); } @Test void generateKeyWithAuthUpperYearShortTitleCapitalizeModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.AUTHOR, AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1); - entry.setField(StandardField.YEAR, "2019"); - entry.setField(StandardField.TITLE, "the InTeresting title longer than THREE words"); + BibEntry entry = new BibEntry() + .withField(StandardField.AUTHOR, AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1) + .withField(StandardField.YEAR, "2019") + .withField(StandardField.TITLE, "the InTeresting title longer than THREE words"); + assertEquals("NEWTON2019InterestingTitleLonger", generateKey(entry, "[auth:upper][year][shorttitle:capitalize]")); } @Test void generateKeyWithYearAuthUpperTitleSentenceCaseModifier() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.AUTHOR, AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3); - entry.setField(StandardField.YEAR, "2019"); - entry.setField(StandardField.TITLE, "the InTeresting title longer than THREE words"); + BibEntry entry = new BibEntry() + .withField(StandardField.AUTHOR, AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_3) + .withField(StandardField.YEAR, "2019") + .withField(StandardField.TITLE, "the InTeresting title longer than THREE words"); + assertEquals("NewtonMaxwellEtAl_2019_TheInterestingTitleLongerThanThreeWords", generateKey(entry, "[authors2]_[year]_[title:capitalize]")); } @Test void generateKeyWithMinusInCitationStyleOutsideAField() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.AUTHOR, AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1); - entry.setField(StandardField.YEAR, "2019"); + BibEntry entry = new BibEntry() + .withField(StandardField.AUTHOR, AUTHOR_STRING_FIRSTNAME_FULL_LASTNAME_FULL_COUNT_1) + .withField(StandardField.YEAR, "2019"); assertEquals("Newton-2019", generateKey(entry, "[auth]-[year]")); } @Test void generateKeyWithWithFirstNCharacters() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.AUTHOR, "Newton, Isaac"); - entry.setField(StandardField.YEAR, "2019"); + BibEntry entry = new BibEntry().withField(StandardField.AUTHOR, "Newton, Isaac") + .withField(StandardField.YEAR, "2019"); assertEquals("newt-2019", generateKey(entry, "[auth4:lower]-[year]")); } + + @Test + void generateKeyCorrectKeyLengthWithTruncateModifierAndUnicode() { + BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Gödel, Kurt"); + + assertEquals(2, generateKey(bibEntry, "[auth:truncate2]").length()); + } + + @Test + void generateKeyCorrectKeyLengthWithAuthNofMthAndUnicode() { + BibEntry bibEntry = new BibEntry().withField(StandardField.AUTHOR, "Gödel, Kurt"); + + assertEquals(4, generateKey(bibEntry, "[auth4_1]").length()); + } } diff --git a/src/test/java/org/jabref/logic/util/io/RegExpBasedFileFinderTests.java b/src/test/java/org/jabref/logic/util/io/RegExpBasedFileFinderTests.java index a7b9cb3a6b7..0cc53fdbc37 100644 --- a/src/test/java/org/jabref/logic/util/io/RegExpBasedFileFinderTests.java +++ b/src/test/java/org/jabref/logic/util/io/RegExpBasedFileFinderTests.java @@ -1,5 +1,6 @@ package org.jabref.logic.util.io; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -95,10 +96,13 @@ void testAuthorWithDiacritics() throws Exception { // when List result = fileFinder.findAssociatedFiles(localEntry, dirs, extensions); + List expected = Collections.singletonList(Path.of("src/test/resources/org/jabref/logic/importer/unlinkedFilesTestFolder/directory/subdirectory/2017_Gražulis_726.pdf")); // then - assertEquals(Collections.singletonList(Path.of("src/test/resources/org/jabref/logic/importer/unlinkedFilesTestFolder/directory/subdirectory/2017_Gražulis_726.pdf")), - result); + assertEquals(expected.size(), result.size()); + for (int i = 0; i < expected.size(); i++) { + assertTrue(Files.isSameFile(expected.get(i), result.get(i))); + } } @Test