diff --git a/CHANGELOG.md b/CHANGELOG.md index 96db4a75227..8d26dbbe27f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We made the command "Push to TexShop" more robust to allow cite commands with a character before the first slash. [forum#2699](https://discourse.jabref.org/t/push-to-texshop-mac/2699/17?u=siedlerchr) - We only show the notification "Saving library..." if the library contains more than 2000 entries. [#9803](https://github.com/JabRef/jabref/issues/9803) - JabRef now keeps previous log files upon start. [#11023](https://github.com/JabRef/jabref/pull/11023) +- When normalizing author names, complete enclosing braces are kept. [#10031](https://github.com/JabRef/jabref/issues/10031) - We enhanced the dialog for adding new fields in the content selector with a selection box containing a list of standard fields. [#10912](https://github.com/JabRef/jabref/pull/10912) - We store the citation relations in an LRU cache to avoid bloating the memory and out-of-memory exceptions. [#10958](https://github.com/JabRef/jabref/issues/10958) - Keywords filed are now displayed as tags. [#10910](https://github.com/JabRef/jabref/pull/10910) diff --git a/src/main/java/org/jabref/gui/autocompleter/PersonNameStringConverter.java b/src/main/java/org/jabref/gui/autocompleter/PersonNameStringConverter.java index c78312638af..f883def6487 100644 --- a/src/main/java/org/jabref/gui/autocompleter/PersonNameStringConverter.java +++ b/src/main/java/org/jabref/gui/autocompleter/PersonNameStringConverter.java @@ -42,11 +42,11 @@ public String toString(Author author) { if (autoCompLF) { switch (autoCompleteFirstNameMode) { case ONLY_ABBREVIATED: - return author.getLastFirst(true); + return author.getFamilyGiven(true); case ONLY_FULL: - return author.getLastFirst(false); + return author.getFamilyGiven(false); case BOTH: - return author.getLastFirst(true); + return author.getFamilyGiven(true); default: break; } @@ -54,16 +54,16 @@ public String toString(Author author) { if (autoCompFF) { switch (autoCompleteFirstNameMode) { case ONLY_ABBREVIATED: - return author.getFirstLast(true); + return author.getGivenFamily(true); case ONLY_FULL: - return author.getFirstLast(false); + return author.getGivenFamily(false); case BOTH: - return author.getFirstLast(true); + return author.getGivenFamily(true); default: break; } } - return author.getLastOnly(); + return author.getNamePrefixAndFamilyName(); } @Override diff --git a/src/main/java/org/jabref/gui/autocompleter/PersonNameSuggestionProvider.java b/src/main/java/org/jabref/gui/autocompleter/PersonNameSuggestionProvider.java index 2ff7d4a4ed1..8457ea5dbd5 100644 --- a/src/main/java/org/jabref/gui/autocompleter/PersonNameSuggestionProvider.java +++ b/src/main/java/org/jabref/gui/autocompleter/PersonNameSuggestionProvider.java @@ -48,7 +48,7 @@ public Stream getAuthors(BibEntry entry) { @Override protected Equivalence getEquivalence() { - return Equivalence.equals().onResultOf(Author::getLastOnly); + return Equivalence.equals().onResultOf(Author::getNamePrefixAndFamilyName); } @Override @@ -58,7 +58,7 @@ protected Comparator getComparator() { @Override protected boolean isMatch(Author candidate, AutoCompletionBinding.ISuggestionRequest request) { - return StringUtil.containsIgnoreCase(candidate.getLastFirst(false), request.getUserText()); + return StringUtil.containsIgnoreCase(candidate.getFamilyGiven(false), request.getUserText()); } @Override diff --git a/src/main/java/org/jabref/logic/bst/util/BstNameFormatter.java b/src/main/java/org/jabref/logic/bst/util/BstNameFormatter.java index cd65735aa57..a49b1527a8d 100644 --- a/src/main/java/org/jabref/logic/bst/util/BstNameFormatter.java +++ b/src/main/java/org/jabref/logic/bst/util/BstNameFormatter.java @@ -102,14 +102,10 @@ public static String formatName(Author author, String format) { char type = control.charAt(0); Optional tokenS = switch (type) { - case 'f' -> - author.getFirst(); - case 'v' -> - author.getVon(); - case 'l' -> - author.getLast(); - case 'j' -> - author.getJr(); + case 'f' -> author.getGivenName(); + case 'v' -> author.getNamePrefix(); + case 'l' -> author.getFamilyName(); + case 'j' -> author.getNameSuffix(); default -> throw new BstVMException("Internal error"); }; diff --git a/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java b/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java index 6d8206dc02e..68b378c2cfe 100644 --- a/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java +++ b/src/main/java/org/jabref/logic/citationkeypattern/BracketedPattern.java @@ -526,17 +526,17 @@ private static AuthorList createAuthorList(String unparsedAuthors) { return AuthorList.parse(unparsedAuthors).getAuthors().stream() .map(author -> { // If the author is an institution, use an institution key instead of the full name - String lastName = author.getLast() + String lastName = author.getFamilyName() .map(lastPart -> isInstitution(author) ? generateInstitutionKey(lastPart) : LatexToUnicodeAdapter.format(lastPart)) .orElse(null); return new Author( - author.getFirst().map(LatexToUnicodeAdapter::format).orElse(null), - author.getFirstAbbr().map(LatexToUnicodeAdapter::format).orElse(null), - author.getVon().map(LatexToUnicodeAdapter::format).orElse(null), + author.getGivenName().map(LatexToUnicodeAdapter::format).orElse(null), + author.getGivenNameAbbreviated().map(LatexToUnicodeAdapter::format).orElse(null), + author.getNamePrefix().map(LatexToUnicodeAdapter::format).orElse(null), lastName, - author.getJr().map(LatexToUnicodeAdapter::format).orElse(null)); + author.getNameSuffix().map(LatexToUnicodeAdapter::format).orElse(null)); }) .collect(AuthorList.collect()); } @@ -548,9 +548,9 @@ private static AuthorList createAuthorList(String unparsedAuthors) { * @return true if only the last name is present and it contains at least one whitespace character. */ private static boolean isInstitution(Author author) { - return author.getFirst().isEmpty() && author.getFirstAbbr().isEmpty() && author.getJr().isEmpty() - && author.getVon().isEmpty() && author.getLast().isPresent() - && WHITESPACE.matcher(author.getLast().get()).find(); + return author.getGivenName().isEmpty() && author.getGivenNameAbbreviated().isEmpty() && author.getNameSuffix().isEmpty() + && author.getNamePrefix().isEmpty() && author.getFamilyName().isPresent() + && WHITESPACE.matcher(author.getFamilyName().get()).find(); } /** @@ -765,7 +765,7 @@ private static String keepLettersAndDigitsOnly(String in) { private static String firstAuthor(AuthorList authorList) { return authorList.getAuthors().stream() .findFirst() - .flatMap(author -> author.getLast().isPresent() ? author.getLast() : author.getVon()) + .flatMap(author -> author.getFamilyName().isPresent() ? author.getFamilyName() : author.getNamePrefix()) .orElse(""); } @@ -779,7 +779,7 @@ private static String firstAuthor(AuthorList authorList) { private static String firstAuthorForenameInitials(AuthorList authorList) { return authorList.getAuthors().stream() .findFirst() - .flatMap(Author::getFirstAbbr) + .flatMap(Author::getGivenNameAbbreviated) .map(s -> s.substring(0, 1)) .orElse(""); } @@ -793,7 +793,7 @@ private static String firstAuthorForenameInitials(AuthorList authorList) { */ private static String firstAuthorVonAndLast(AuthorList authorList) { return authorList.isEmpty() ? "" : - authorList.getAuthor(0).getLastOnly().replace(" ", ""); + authorList.getAuthor(0).getNamePrefixAndFamilyName().replace(" ", ""); } /** @@ -806,7 +806,7 @@ private static String lastAuthor(AuthorList authorList) { if (authorList.isEmpty()) { return ""; } - return authorList.getAuthors().get(authorList.getNumberOfAuthors() - 1).getLast().orElse(""); + return authorList.getAuthors().get(authorList.getNumberOfAuthors() - 1).getFamilyName().orElse(""); } /** @@ -820,7 +820,7 @@ private static String lastAuthorForenameInitials(AuthorList authorList) { if (authorList.isEmpty()) { return ""; } - return authorList.getAuthor(authorList.getNumberOfAuthors() - 1).getFirstAbbr().map(s -> s.substring(0, 1)) + return authorList.getAuthor(authorList.getNumberOfAuthors() - 1).getGivenNameAbbreviated().map(s -> s.substring(0, 1)) .orElse(""); } @@ -857,7 +857,7 @@ static String authorsAlpha(AuthorList authorList) { } if (authorList.getNumberOfAuthors() == 1) { - String[] firstAuthor = authorList.getAuthor(0).getLastOnly() + String[] firstAuthor = authorList.getAuthor(0).getNamePrefixAndFamilyName() .replaceAll("\\s+", " ").trim().split(" "); // take first letter of any "prefixes" (e.g. van der Aalst -> vd) for (int j = 0; j < (firstAuthor.length - 1); j++) { @@ -873,7 +873,7 @@ static String authorsAlpha(AuthorList authorList) { } List vonAndLastNames = authorList.getAuthors().stream() .limit(maxAuthors) - .map(Author::getLastOnly) + .map(Author::getNamePrefixAndFamilyName) .collect(Collectors.toList()); for (String vonAndLast : vonAndLastNames) { // replace all whitespaces by " " @@ -913,7 +913,7 @@ private static String joinAuthorsOnLastName(AuthorList authorList, int maxAuthor return Optional.of(suffix); } } else { - return author.getLast(); + return author.getFamilyName(); } }) .flatMap(Optional::stream) @@ -970,7 +970,7 @@ static String authEtal(AuthorList authorList, String delim, String append) { // exception: If the second author is "and others", then do the appendix handling (in the other branch) return joinAuthorsOnLastName(authorList, 2, delim, ""); } else { - return authorList.getAuthor(0).getLast().orElse("") + append; + return authorList.getAuthor(0).getFamilyName().orElse("") + append; } } @@ -990,7 +990,7 @@ private static String authNofMth(AuthorList authorList, int n, int m) { if (lastAuthor.equals(Author.OTHERS)) { return "+"; } - String lastName = lastAuthor.getLast() + String lastName = lastAuthor.getFamilyName() .map(CitationKeyGenerator::removeDefaultUnwantedCharacters).orElse(""); return lastName.length() > n ? lastName.substring(0, n) : lastName; } @@ -1010,7 +1010,7 @@ static String authShort(AuthorList authorList) { final int numberOfAuthors = authorList.getNumberOfAuthors(); if (numberOfAuthors == 1) { - author.append(authorList.getAuthor(0).getLast().orElse("")); + author.append(authorList.getAuthor(0).getFamilyName().orElse("")); } else if (numberOfAuthors >= 2) { for (int i = 0; (i < numberOfAuthors) && (i < 3); i++) { author.append(authNofMth(authorList, 1, i + 1)); diff --git a/src/main/java/org/jabref/logic/exporter/MSBibExporter.java b/src/main/java/org/jabref/logic/exporter/MSBibExporter.java index 1a1da69dab1..ea80cefc7f4 100644 --- a/src/main/java/org/jabref/logic/exporter/MSBibExporter.java +++ b/src/main/java/org/jabref/logic/exporter/MSBibExporter.java @@ -19,33 +19,37 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jspecify.annotations.NonNull; + /** * TemplateExporter for exporting in MSBIB XML format. */ class MSBibExporter extends Exporter { + private final TransformerFactory transformerFactory; + public MSBibExporter() { super("MSBib", "MS Office 2007", StandardFileType.XML); + transformerFactory = TransformerFactory.newInstance(); } @Override - public void export(final BibDatabaseContext databaseContext, final Path file, - List entries) throws SaveException { - Objects.requireNonNull(databaseContext); - Objects.requireNonNull(entries); - + public void export(@NonNull BibDatabaseContext databaseContext, + @NonNull Path file, + @NonNull List entries) throws SaveException { + Objects.requireNonNull(databaseContext); // required by test case if (entries.isEmpty()) { return; } MSBibDatabase msBibDatabase = new MSBibDatabase(databaseContext.getDatabase(), entries); - // forcing to use UTF8 output format for some problems with xml export in other encodings + // forcing to use UTF8 output format for some problems with XML export in other encodings try (AtomicFileWriter ps = new AtomicFileWriter(file, StandardCharsets.UTF_8)) { try { DOMSource source = new DOMSource(msBibDatabase.getDomForExport()); StreamResult result = new StreamResult(ps); - Transformer trans = TransformerFactory.newInstance().newTransformer(); + Transformer trans = transformerFactory.newTransformer(); trans.setOutputProperty(OutputKeys.INDENT, "yes"); trans.transform(source, result); } catch (TransformerException | IllegalArgumentException | TransformerFactoryConfigurationError e) { diff --git a/src/main/java/org/jabref/logic/formatter/bibtexfields/HtmlToLatexFormatter.java b/src/main/java/org/jabref/logic/formatter/bibtexfields/HtmlToLatexFormatter.java index 33664983425..a9db09f1e4f 100644 --- a/src/main/java/org/jabref/logic/formatter/bibtexfields/HtmlToLatexFormatter.java +++ b/src/main/java/org/jabref/logic/formatter/bibtexfields/HtmlToLatexFormatter.java @@ -13,6 +13,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * The inverse operation is "somehow" contained in {@link org.jabref.logic.openoffice.style.OOPreFormatter} + */ public class HtmlToLatexFormatter extends Formatter implements LayoutFormatter { private static final Logger LOGGER = LoggerFactory.getLogger(HtmlToLatexFormatter.class); diff --git a/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatter.java b/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatter.java index fdbc4f21d09..c29b081cc48 100644 --- a/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatter.java +++ b/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatter.java @@ -1,10 +1,11 @@ package org.jabref.logic.formatter.bibtexfields; -import java.util.Objects; - import org.jabref.logic.cleanup.Formatter; import org.jabref.logic.l10n.Localization; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class RemoveBracesFormatter extends Formatter { @Override @@ -19,8 +20,6 @@ public String getKey() { @Override public String format(String value) { - Objects.requireNonNull(value); - String formatted = value; while ((formatted.length() >= 2) && (formatted.charAt(0) == '{') && (formatted.charAt(formatted.length() - 1) == '}')) { @@ -50,7 +49,7 @@ public String getExampleInput() { /** * Check if a string at any point has had more ending } braces than opening { ones. - * Will e.g. return true for the string "DNA} blahblal {EPA" + * Will e.g. return true for the string "DNA} text {EPA" * * @param value The string to check. * @return true if at any index the brace count is negative. diff --git a/src/main/java/org/jabref/logic/importer/AuthorListParser.java b/src/main/java/org/jabref/logic/importer/AuthorListParser.java index 874ec1525ad..6ef699d3db1 100644 --- a/src/main/java/org/jabref/logic/importer/AuthorListParser.java +++ b/src/main/java/org/jabref/logic/importer/AuthorListParser.java @@ -6,7 +6,6 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -14,6 +13,8 @@ import org.jabref.model.entry.AuthorList; import org.jabref.model.strings.StringUtil; +import org.jspecify.annotations.NonNull; + public class AuthorListParser { // Avoid partition where these values are contained @@ -94,9 +95,7 @@ private static StringBuilder buildWithAffix(Collection indexArray, List * @param listOfNames the String containing the person names to be parsed * @return a parsed list of persons */ - public AuthorList parse(String listOfNames) { - Objects.requireNonNull(listOfNames); - + public AuthorList parse(@NonNull String listOfNames) { // Handling of "and others" // Remove it from the list; it will be added at the very end of this method as special Author.OTHERS listOfNames = listOfNames.trim(); diff --git a/src/main/java/org/jabref/logic/importer/Importer.java b/src/main/java/org/jabref/logic/importer/Importer.java index d2a8a24ec36..fa4159dc404 100644 --- a/src/main/java/org/jabref/logic/importer/Importer.java +++ b/src/main/java/org/jabref/logic/importer/Importer.java @@ -36,7 +36,7 @@ public abstract class Importer implements Comparable { * The effect of this method is primarily to avoid unnecessary processing of files when searching for a suitable * import format. If this method returns false, the import routine will move on to the next import format. *

- * Thus the correct behaviour is to return false if it is certain that the file is not of the suitable type, and + * Thus, the correct behaviour is to return false if it is certain that the file is not of the suitable type, and * true otherwise. Returning true is the safe choice if not certain. */ public abstract boolean isRecognizedFormat(BufferedReader input) throws IOException; diff --git a/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java b/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java index f1b0b5fb32c..70afa7847c1 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java @@ -71,7 +71,7 @@ public URL getURLForEntry(BibEntry entry) throws URISyntaxException, MalformedUR // replace "and" by ";" as citation matching API uses ";" for separation AuthorList authors = AuthorList.parse(entry.getFieldOrAlias(StandardField.AUTHOR).get()); String authorsWithSemicolon = authors.getAuthors().stream() - .map(author -> author.getLastFirst(false)) + .map(author -> author.getFamilyGiven(false)) .collect(Collectors.joining(";")); uriBuilder.addParameter("a", authorsWithSemicolon); } diff --git a/src/main/java/org/jabref/logic/importer/fileformat/MsBibImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/MsBibImporter.java index 31863a621eb..f162f5e9c84 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/MsBibImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/MsBibImporter.java @@ -13,6 +13,7 @@ import org.jabref.logic.msbib.MSBibDatabase; import org.jabref.logic.util.StandardFileType; +import org.jspecify.annotations.NullMarked; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -24,24 +25,25 @@ /** * Importer for the MS Office 2007 XML bibliography format */ +@NullMarked public class MsBibImporter extends Importer { private static final Logger LOGGER = LoggerFactory.getLogger(MsBibImporter.class); private static final String DISABLEDTD = "http://apache.org/xml/features/disallow-doctype-decl"; private static final String DISABLEEXTERNALDTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = makeSafeDocBuilderFactory(DocumentBuilderFactory.newInstance()); + /** + * The correct behavior is to return false if it is certain that the file is + * not of the MsBib type, and true otherwise. Returning true is the safe choice + * if not certain. + */ @Override public boolean isRecognizedFormat(BufferedReader reader) throws IOException { - Objects.requireNonNull(reader); - - /* - The correct behavior is to return false if it is certain that the file is - not of the MsBib type, and true otherwise. Returning true is the safe choice - if not certain. - */ + Objects.requireNonNull(reader); // Required by test case Document docin; try { - DocumentBuilder dbuild = makeSafeDocBuilderFactory(DocumentBuilderFactory.newInstance()).newDocumentBuilder(); + DocumentBuilder dbuild = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); dbuild.setErrorHandler(new ErrorHandler() { @Override public void warning(SAXParseException exception) throws SAXException { @@ -68,8 +70,7 @@ public void error(SAXParseException exception) throws SAXException { @Override public ParserResult importDatabase(BufferedReader reader) throws IOException { - Objects.requireNonNull(reader); - + Objects.requireNonNull(reader); // Required by test case MSBibDatabase dbase = new MSBibDatabase(); return new ParserResult(dbase.importEntriesFromXml(reader)); } @@ -96,7 +97,7 @@ public String getDescription() { * @param dBuild | DocumentBuilderFactory to be made XXE safe. * @return If supported, XXE safe DocumentBuilderFactory. Else, returns original builder given */ - private DocumentBuilderFactory makeSafeDocBuilderFactory(DocumentBuilderFactory dBuild) { + private static DocumentBuilderFactory makeSafeDocBuilderFactory(DocumentBuilderFactory dBuild) { String feature = null; try { diff --git a/src/main/java/org/jabref/logic/layout/format/AuthorOrgSci.java b/src/main/java/org/jabref/logic/layout/format/AuthorOrgSci.java index 4671220df73..75bc76821f4 100644 --- a/src/main/java/org/jabref/logic/layout/format/AuthorOrgSci.java +++ b/src/main/java/org/jabref/logic/layout/format/AuthorOrgSci.java @@ -30,9 +30,9 @@ public String format(String fieldText) { } Author first = a.getAuthor(0); StringBuilder sb = new StringBuilder(); - sb.append(first.getLastFirst(true)); + sb.append(first.getFamilyGiven(true)); for (int i = 1; i < a.getNumberOfAuthors(); i++) { - sb.append(", ").append(a.getAuthor(i).getFirstLast(true)); + sb.append(", ").append(a.getAuthor(i).getGivenFamily(true)); } return sb.toString(); } diff --git a/src/main/java/org/jabref/logic/layout/format/Authors.java b/src/main/java/org/jabref/logic/layout/format/Authors.java index 5692470fac3..dd6bef7ad54 100644 --- a/src/main/java/org/jabref/logic/layout/format/Authors.java +++ b/src/main/java/org/jabref/logic/layout/format/Authors.java @@ -254,21 +254,21 @@ public String format(String fieldText) { private void addSingleName(StringBuilder sb, Author a, boolean firstFirst) { StringBuilder lastNameSB = new StringBuilder(); - a.getVon().filter(von -> !von.isEmpty()).ifPresent(von -> lastNameSB.append(von).append(' ')); - a.getLast().ifPresent(lastNameSB::append); + a.getNamePrefix().filter(von -> !von.isEmpty()).ifPresent(von -> lastNameSB.append(von).append(' ')); + a.getFamilyName().ifPresent(lastNameSB::append); String jrSeparator = " "; - a.getJr().filter(jr -> !jr.isEmpty()).ifPresent(jr -> lastNameSB.append(jrSeparator).append(jr)); + a.getNameSuffix().filter(jr -> !jr.isEmpty()).ifPresent(jr -> lastNameSB.append(jrSeparator).append(jr)); String firstNameResult = ""; - if (a.getFirst().isPresent()) { + if (a.getGivenName().isPresent()) { if (abbreviate) { - firstNameResult = a.getFirstAbbr().orElse(""); + firstNameResult = a.getGivenNameAbbreviated().orElse(""); if (firstInitialOnly && (firstNameResult.length() > 2)) { firstNameResult = firstNameResult.substring(0, 2); } else if (middleInitial) { String abbr = firstNameResult; - firstNameResult = a.getFirst().get(); + firstNameResult = a.getGivenName().get(); int index = firstNameResult.indexOf(' '); if (index >= 0) { firstNameResult = firstNameResult.substring(0, index + 1); @@ -284,7 +284,7 @@ private void addSingleName(StringBuilder sb, Author a, boolean firstFirst) { firstNameResult = firstNameResult.replace(" ", ""); } } else { - firstNameResult = a.getFirst().get(); + firstNameResult = a.getGivenName().get(); } } diff --git a/src/main/java/org/jabref/logic/layout/format/DocBookAuthorFormatter.java b/src/main/java/org/jabref/logic/layout/format/DocBookAuthorFormatter.java index 01bdfe29c46..9d1b426eb81 100644 --- a/src/main/java/org/jabref/logic/layout/format/DocBookAuthorFormatter.java +++ b/src/main/java/org/jabref/logic/layout/format/DocBookAuthorFormatter.java @@ -20,13 +20,13 @@ public void addBody(StringBuilder sb, AuthorList al, String tagName, DocBookVers sb.append(""); } Author a = al.getAuthor(i); - a.getFirst().filter(first -> !first.isEmpty()).ifPresent(first -> sb.append("") - .append(XML_CHARS.format(first)).append("")); - a.getVon().filter(von -> !von.isEmpty()).ifPresent(von -> sb.append("") - .append(XML_CHARS.format(von)).append("")); - a.getLast().filter(last -> !last.isEmpty()).ifPresent(last -> { + a.getGivenName().filter(first -> !first.isEmpty()).ifPresent(first -> sb.append("") + .append(XML_CHARS.format(first)).append("")); + a.getNamePrefix().filter(von -> !von.isEmpty()).ifPresent(von -> sb.append("") + .append(XML_CHARS.format(von)).append("")); + a.getFamilyName().filter(last -> !last.isEmpty()).ifPresent(last -> { sb.append("").append(XML_CHARS.format(last)); - a.getJr().filter(jr -> !jr.isEmpty()) + a.getNameSuffix().filter(jr -> !jr.isEmpty()) .ifPresent(jr -> sb.append(' ').append(XML_CHARS.format(jr))); sb.append(""); if (version == DocBookVersion.DOCBOOK_5) { diff --git a/src/main/java/org/jabref/logic/msbib/BibTeXConverter.java b/src/main/java/org/jabref/logic/msbib/BibTeXConverter.java index 6881252d8d0..39d5af3a42b 100644 --- a/src/main/java/org/jabref/logic/msbib/BibTeXConverter.java +++ b/src/main/java/org/jabref/logic/msbib/BibTeXConverter.java @@ -24,9 +24,6 @@ private BibTeXConverter() { /** * Converts an {@link MSBibEntry} to a {@link BibEntry} for import - * - * @param entry The MsBibEntry to convert - * @return The bib entry */ public static BibEntry convert(MSBibEntry entry) { BibEntry result; diff --git a/src/main/java/org/jabref/logic/msbib/MSBibConverter.java b/src/main/java/org/jabref/logic/msbib/MSBibConverter.java index 6812d1033de..d565195e6fc 100644 --- a/src/main/java/org/jabref/logic/msbib/MSBibConverter.java +++ b/src/main/java/org/jabref/logic/msbib/MSBibConverter.java @@ -2,8 +2,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; -import org.jabref.model.entry.Author; +import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.Month; @@ -17,6 +18,7 @@ public class MSBibConverter { private static final String MSBIB_PREFIX = "msbib-"; private static final String BIBTEX_PREFIX = "BIBTEX_"; + private static final RemoveBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveBracesFormatter(); private MSBibConverter() { } @@ -123,24 +125,26 @@ public static MSBibEntry convert(BibEntry entry) { private static List getAuthors(BibEntry entry, String authors, Field field) { List result = new ArrayList<>(); - boolean corporate = false; + // Only one corporate author is supported - // We have the possible rare case that are multiple authors which start and end with latex , this is currently not considered - if (authors.startsWith("{") && authors.endsWith("}")) { - corporate = true; + // Heuristics: If the author is surrounded by curly braces, it is a corporate author + boolean corporate = !REMOVE_BRACES_FORMATTER.format(authors).equals(authors); + + Optional authorLatexFreeOpt = entry.getFieldLatexFree(field); + if (authorLatexFreeOpt.isEmpty()) { + return result; } - // FIXME: #4152 This is an ugly hack because the latex2unicode formatter kills of all curly braces, so no more corporate author parsing possible - String authorLatexFree = entry.getFieldLatexFree(field).orElse(""); + String authorLatexFree = authorLatexFreeOpt.get(); + + // We re-add the curly braces to keep the corporate author as is. + // See https://github.com/JabRef/jabref-issue-melting-pot/issues/386 for details if (corporate) { authorLatexFree = "{" + authorLatexFree + "}"; } - AuthorList authorList = AuthorList.parse(authorLatexFree); - - for (Author author : authorList.getAuthors()) { - result.add(new MsBibAuthor(author, corporate)); - } - - return result; + return AuthorList.parse(authorLatexFree).getAuthors() + .stream() + .map(author -> new MsBibAuthor(author, corporate)) + .toList(); } } diff --git a/src/main/java/org/jabref/logic/msbib/MSBibDatabase.java b/src/main/java/org/jabref/logic/msbib/MSBibDatabase.java index f2040674fef..10a646f401d 100644 --- a/src/main/java/org/jabref/logic/msbib/MSBibDatabase.java +++ b/src/main/java/org/jabref/logic/msbib/MSBibDatabase.java @@ -36,44 +36,46 @@ public class MSBibDatabase { private static final Logger LOGGER = LoggerFactory.getLogger(MSBibDatabase.class); - private Set entries; + private final DocumentBuilderFactory factory; + + private Set entriesForExport; /** * Creates a {@link MSBibDatabase} for import */ public MSBibDatabase() { - entries = new HashSet<>(); + entriesForExport = new HashSet<>(); + factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); } - // TODO: why an additonal entry list? entries are included inside database! - /** - * Creates a new {@link MSBibDatabase} for export + * Creates a new {@link MSBibDatabase} for export. + * Directly converts the given entries. * * @param database The bib database * @param entries List of {@link BibEntry} */ public MSBibDatabase(BibDatabase database, List entries) { + this(); if (entries == null) { var resolvedEntries = database.resolveForStrings(database.getEntries(), false); - addEntriesForExport(resolvedEntries); + setEntriesForExport(resolvedEntries); } else { var resolvedEntries = database.resolveForStrings(entries, false); - addEntriesForExport(resolvedEntries); + setEntriesForExport(resolvedEntries); } } /** - * Imports entries from an office xml file + * Imports entries from an office XML file * * @return List of {@link BibEntry} */ public List importEntriesFromXml(BufferedReader reader) { - entries = new HashSet<>(); + entriesForExport = new HashSet<>(); Document inputDocument; try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); DocumentBuilder documentBuilder = factory.newDocumentBuilder(); inputDocument = documentBuilder.parse(new InputSource(reader)); } catch (ParserConfigurationException | SAXException | IOException e) { @@ -92,31 +94,24 @@ public List importEntriesFromXml(BufferedReader reader) { NodeList sourceList = ((Element) rootList.item(0)).getElementsByTagNameNS("*", "Source"); for (int i = 0; i < sourceList.getLength(); i++) { MSBibEntry entry = new MSBibEntry((Element) sourceList.item(i)); - entries.add(entry); + entriesForExport.add(entry); bibitems.add(BibTeXConverter.convert(entry)); } return bibitems; } - private void addEntriesForExport(List entriesToAdd) { - entries = new HashSet<>(); + private void setEntriesForExport(List entriesToAdd) { + entriesForExport = new HashSet<>(); for (BibEntry entry : entriesToAdd) { MSBibEntry newMods = MSBibConverter.convert(entry); - entries.add(newMods); + entriesForExport.add(newMods); } } - /** - * Gets the assembled dom for export - * - * @return XML Document - */ public Document getDomForExport() { Document document = null; try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); DocumentBuilder documentBuilder = factory.newDocumentBuilder(); document = documentBuilder.newDocument(); @@ -126,7 +121,7 @@ public Document getDomForExport() { "xmlns:" + PREFIX.substring(0, PREFIX.length() - 1), NAMESPACE); rootNode.setAttribute("SelectedStyle", ""); - for (MSBibEntry entry : entries) { + for (MSBibEntry entry : entriesForExport) { Node node = entry.getEntryDom(document); rootNode.appendChild(node); } diff --git a/src/main/java/org/jabref/logic/msbib/MSBibEntry.java b/src/main/java/org/jabref/logic/msbib/MSBibEntry.java index 28618a6f60e..2366c3a5159 100644 --- a/src/main/java/org/jabref/logic/msbib/MSBibEntry.java +++ b/src/main/java/org/jabref/logic/msbib/MSBibEntry.java @@ -28,8 +28,8 @@ */ class MSBibEntry { - // MSBib fields and values public Map fields = new HashMap<>(); + public List authors; public List bookAuthors; public List editors; @@ -74,7 +74,6 @@ class MSBibEntry { * Matches both single locations (only city) like Berlin and full locations like Stroudsburg, PA, USA
* tested using http://www.regexpal.com/ */ - private final Pattern ADDRESS_PATTERN = Pattern.compile("\\b(\\w+)\\s?[,]?\\s?(\\w*)\\s?[,]?\\s?(\\w*)\\b"); public MSBibEntry() { @@ -82,7 +81,7 @@ public MSBibEntry() { } /** - * Create a new {@link MsBibEntry} to import from an xml element + * Create a new {@link MSBibEntry} to import from an XML element */ public MSBibEntry(Element entry) { populateFromXml(entry); @@ -311,14 +310,14 @@ private void addAuthor(Document document, Element allAuthors, String entryName, } Element authorTop = document.createElementNS(MSBibDatabase.NAMESPACE, MSBibDatabase.PREFIX + entryName); - Optional personName = authorsLst.stream().filter(MsBibAuthor::isCorporate) + Optional personName = authorsLst.stream() + .filter(MsBibAuthor::isCorporate) .findFirst(); if (personName.isPresent()) { MsBibAuthor person = personName.get(); - Element corporate = document.createElementNS(MSBibDatabase.NAMESPACE, MSBibDatabase.PREFIX + "Corporate"); - corporate.setTextContent(person.getFirstLast()); + corporate.setTextContent(person.getLastName()); authorTop.appendChild(corporate); } else { Element nameList = document.createElementNS(MSBibDatabase.NAMESPACE, MSBibDatabase.PREFIX + "NameList"); diff --git a/src/main/java/org/jabref/logic/msbib/MSBibMapping.java b/src/main/java/org/jabref/logic/msbib/MSBibMapping.java index d5abe4dbc15..ce27cdcf381 100644 --- a/src/main/java/org/jabref/logic/msbib/MSBibMapping.java +++ b/src/main/java/org/jabref/logic/msbib/MSBibMapping.java @@ -22,13 +22,13 @@ public class MSBibMapping { private static final String BIBTEX_PREFIX = "BIBTEX_"; private static final String MSBIB_PREFIX = "msbib-"; - - private static final BiMap BIBLATEX_TO_MS_BIB = HashBiMap.create(); - - // see https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a private static final BiMap LANG_TO_LCID = HashBiMap.create(); + private static final BiMap BIBLATEX_TO_MS_BIB = HashBiMap.create(); + private static final Map MSBIB_ENTRYTYPE_MAPPING; + private static final Map BIB_ENTRYTYPE_MAPPING = new HashMap<>(); static { + // see https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a LANG_TO_LCID.put("basque", 1609); LANG_TO_LCID.put("bulgarian", 1026); LANG_TO_LCID.put("catalan", 1027); @@ -120,47 +120,49 @@ public class MSBibMapping { BIBLATEX_TO_MS_BIB.put(new UnknownField(MSBIB_PREFIX + "productioncompany"), "ProductionCompany"); } + static { + MSBIB_ENTRYTYPE_MAPPING = Map.of( + "Book", StandardEntryType.Book, + "BookSection", StandardEntryType.Book, + "JournalArticle", StandardEntryType.Article, + "ArticleInAPeriodical", IEEETranEntryType.Periodical, + "ConferenceProceedings", StandardEntryType.InProceedings, + "Report", StandardEntryType.TechReport, + "Patent", IEEETranEntryType.Patent, + "InternetSite", StandardEntryType.Online); + } + + static { + // We need to add the entries "manually", because Map.of does not allow that many entries + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Book, MSBibEntryType.Book); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.InBook, MSBibEntryType.BookSection); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Booklet, MSBibEntryType.BookSection); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.InCollection, MSBibEntryType.BookSection); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Article, MSBibEntryType.JournalArticle); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.InProceedings, MSBibEntryType.ConferenceProceedings); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Conference, MSBibEntryType.ConferenceProceedings); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Proceedings, MSBibEntryType.ConferenceProceedings); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Collection, MSBibEntryType.ConferenceProceedings); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.TechReport, MSBibEntryType.Report); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Manual, MSBibEntryType.Report); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.MastersThesis, MSBibEntryType.Report); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.PhdThesis, MSBibEntryType.Report); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Unpublished, MSBibEntryType.Report); + BIB_ENTRYTYPE_MAPPING.put(IEEETranEntryType.Patent, MSBibEntryType.Patent); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Misc, MSBibEntryType.Misc); + BIB_ENTRYTYPE_MAPPING.put(IEEETranEntryType.Electronic, MSBibEntryType.ElectronicSource); + BIB_ENTRYTYPE_MAPPING.put(StandardEntryType.Online, MSBibEntryType.InternetSite); + } + private MSBibMapping() { } public static EntryType getBiblatexEntryType(String msbibType) { - Map entryTypeMapping = new HashMap<>(); - - entryTypeMapping.put("Book", StandardEntryType.Book); - entryTypeMapping.put("BookSection", StandardEntryType.Book); - entryTypeMapping.put("JournalArticle", StandardEntryType.Article); - entryTypeMapping.put("ArticleInAPeriodical", IEEETranEntryType.Periodical); - entryTypeMapping.put("ConferenceProceedings", StandardEntryType.InProceedings); - entryTypeMapping.put("Report", StandardEntryType.TechReport); - entryTypeMapping.put("Patent", IEEETranEntryType.Patent); - entryTypeMapping.put("InternetSite", StandardEntryType.Online); - - return entryTypeMapping.getOrDefault(msbibType, StandardEntryType.Misc); + return MSBIB_ENTRYTYPE_MAPPING.getOrDefault(msbibType, StandardEntryType.Misc); } public static MSBibEntryType getMSBibEntryType(EntryType bibtexType) { - Map entryTypeMapping = new HashMap<>(); - - entryTypeMapping.put(StandardEntryType.Book, MSBibEntryType.Book); - entryTypeMapping.put(StandardEntryType.InBook, MSBibEntryType.BookSection); - entryTypeMapping.put(StandardEntryType.Booklet, MSBibEntryType.BookSection); - entryTypeMapping.put(StandardEntryType.InCollection, MSBibEntryType.BookSection); - entryTypeMapping.put(StandardEntryType.Article, MSBibEntryType.JournalArticle); - entryTypeMapping.put(StandardEntryType.InProceedings, MSBibEntryType.ConferenceProceedings); - entryTypeMapping.put(StandardEntryType.Conference, MSBibEntryType.ConferenceProceedings); - entryTypeMapping.put(StandardEntryType.Proceedings, MSBibEntryType.ConferenceProceedings); - entryTypeMapping.put(StandardEntryType.Collection, MSBibEntryType.ConferenceProceedings); - entryTypeMapping.put(StandardEntryType.TechReport, MSBibEntryType.Report); - entryTypeMapping.put(StandardEntryType.Manual, MSBibEntryType.Report); - entryTypeMapping.put(StandardEntryType.MastersThesis, MSBibEntryType.Report); - entryTypeMapping.put(StandardEntryType.PhdThesis, MSBibEntryType.Report); - entryTypeMapping.put(StandardEntryType.Unpublished, MSBibEntryType.Report); - entryTypeMapping.put(IEEETranEntryType.Patent, MSBibEntryType.Patent); - entryTypeMapping.put(StandardEntryType.Misc, MSBibEntryType.Misc); - entryTypeMapping.put(IEEETranEntryType.Electronic, MSBibEntryType.ElectronicSource); - entryTypeMapping.put(StandardEntryType.Online, MSBibEntryType.InternetSite); - - return entryTypeMapping.getOrDefault(bibtexType, MSBibEntryType.Misc); + return BIB_ENTRYTYPE_MAPPING.getOrDefault(bibtexType, MSBibEntryType.Misc); } /** diff --git a/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java b/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java index 1c1bd488390..bcceef9657f 100644 --- a/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java +++ b/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java @@ -1,9 +1,12 @@ package org.jabref.logic.msbib; +import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; import org.jabref.model.entry.Author; public class MsBibAuthor { + private static final RemoveBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveBracesFormatter(); + private String firstName; private String middleName; private final Author author; @@ -13,7 +16,7 @@ public MsBibAuthor(Author author) { this.author = author; StringBuilder sb = new StringBuilder(); - author.getFirst().ifPresent(firstNames -> { + author.getGivenName().ifPresent(firstNames -> { String[] names = firstNames.split(" "); for (int i = 1; i < names.length; i++) { @@ -34,7 +37,7 @@ public String getFirstName() { if (!"".equals(firstName)) { return firstName; } - return author.getFirst().orElse(null); + return author.getGivenName().orElse(null); } public String getMiddleName() { @@ -45,15 +48,15 @@ public String getMiddleName() { } public String getLastName() { - return author.getLastOnly(); + return REMOVE_BRACES_FORMATTER.format(author.getNamePrefixAndFamilyName()); } public String getFirstLast() { - return author.getFirstLast(false); + return author.getGivenFamily(false); } public String getLastFirst() { - return author.getLastFirst(false); + return author.getFamilyGiven(false); } public boolean isCorporate() { diff --git a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java index f788c809474..c38e3d37ed0 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java @@ -3,9 +3,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Optional; +import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.Author; import org.jabref.model.entry.AuthorList; @@ -20,8 +20,12 @@ import org.jabref.model.openoffice.style.PageInfo; import org.jabref.model.strings.StringUtil; +import org.jspecify.annotations.NonNull; + class OOBibStyleGetCitationMarker { + private static final RemoveBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveBracesFormatter(); + private OOBibStyleGetCitationMarker() { } @@ -42,13 +46,13 @@ private static String getAuthorLastName(AuthorList authorList, int number) { if (authorList.getNumberOfAuthors() > number) { Author author = authorList.getAuthor(number); // "von " if von exists - Optional von = author.getVon(); + Optional von = author.getNamePrefix(); if (von.isPresent() && !von.get().isEmpty()) { stringBuilder.append(von.get()); stringBuilder.append(' '); } // last name if it exists - stringBuilder.append(author.getLast().orElse("")); + stringBuilder.append(author.getFamilyName().map(last -> REMOVE_BRACES_FORMATTER.format(last)).orElse("")); } return stringBuilder.toString(); @@ -87,12 +91,9 @@ private static String markupAuthorName(OOBibStyle style, String name) { * - andString is only emitted if nAuthors is at least 2. */ private static String formatAuthorList(OOBibStyle style, - AuthorList authorList, + @NonNull AuthorList authorList, int maxAuthors, String andString) { - - Objects.requireNonNull(authorList); - // Apparently maxAuthorsBeforeEtAl is always 1 for in-text citations. // In reference lists can be for example 7, // (https://www.chicagomanualofstyle.org/turabian/turabian-author-date-citation-quick-guide.html) @@ -224,16 +225,13 @@ private static class FieldAndContent { * field (or alias) from {@code fields} found in {@code entry}. * Return {@code Optional.empty()} if found nothing. */ - private static Optional getRawCitationMarkerField(BibEntry entry, - BibDatabase database, - OrFields fields) { - Objects.requireNonNull(entry, "Entry cannot be null"); - Objects.requireNonNull(database, "database cannot be null"); - + private static Optional getRawCitationMarkerField(@NonNull BibEntry entry, + @NonNull BibDatabase database, + @NonNull OrFields fields) { for (Field field : fields.getFields() /* FieldFactory.parseOrFields(fields)*/) { + // NOT LaTeX free, because there is some latextohtml in org.jabref.logic.openoffice.style.OOPreFormatter Optional optionalContent = entry.getResolvedFieldOrAlias(field, database); - final boolean foundSomething = optionalContent.isPresent() - && !optionalContent.get().trim().isEmpty(); + final boolean foundSomething = !StringUtil.isBlank(optionalContent); if (foundSomething) { return Optional.of(new FieldAndContent(field, optionalContent.get())); } @@ -264,10 +262,8 @@ private static Optional getRawCitationMarkerField(BibEntry entr * */ private static String getCitationMarkerField(OOBibStyle style, - CitationLookupResult db, + @NonNull CitationLookupResult db, OrFields fields) { - Objects.requireNonNull(db); - Optional optionalFieldAndContent = getRawCitationMarkerField(db.entry, db.database, fields); diff --git a/src/main/java/org/jabref/logic/xmp/DublinCoreExtractor.java b/src/main/java/org/jabref/logic/xmp/DublinCoreExtractor.java index 13ea76677b1..8e0cfcf20d9 100644 --- a/src/main/java/org/jabref/logic/xmp/DublinCoreExtractor.java +++ b/src/main/java/org/jabref/logic/xmp/DublinCoreExtractor.java @@ -299,7 +299,7 @@ public Optional extractBibtexEntry() { private void fillContributor(String authors) { AuthorList list = AuthorList.parse(authors); for (Author author : list.getAuthors()) { - dcSchema.addContributor(author.getFirstLast(false)); + dcSchema.addContributor(author.getGivenFamily(false)); } } @@ -309,7 +309,7 @@ private void fillContributor(String authors) { private void fillCreator(String creators) { AuthorList list = AuthorList.parse(creators); for (Author author : list.getAuthors()) { - dcSchema.addCreator(author.getFirstLast(false)); + dcSchema.addCreator(author.getGivenFamily(false)); } } diff --git a/src/main/java/org/jabref/model/entry/Author.java b/src/main/java/org/jabref/model/entry/Author.java index fd45ddb1e05..e4511e7ef07 100644 --- a/src/main/java/org/jabref/model/entry/Author.java +++ b/src/main/java/org/jabref/model/entry/Author.java @@ -20,28 +20,38 @@ public class Author { */ public static final Author OTHERS = new Author("", "", null, "others", null); - private final String firstPart; - private final String firstAbbr; - private final String vonPart; - private final String lastPart; - private final String jrPart; + private final String givenName; + private final String givenNameAbbreviated; + private final String namePrefix; + private final String familyName; + private final String nameSuffix; private Author latexFreeAuthor; /** * Creates the Author object. If any part of the name is absent, null must be passed; otherwise other methods may return erroneous results. + *

+ * In case only the last part is passed, enclosing braces are * - * @param first the first name of the author (may consist of several tokens, like "Charles Louis Xavier Joseph" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") - * @param firstabbr the abbreviated first name of the author (may consist of several tokens, like "C. L. X. J." in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin"). It is a responsibility of the caller to create a reasonable abbreviation of the first name. - * @param von the von part of the author's name (may consist of several tokens, like "de la" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") - * @param last the last name of the author (may consist of several tokens, like "Vall{\'e}e Poussin" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") - * @param jr the junior part of the author's name (may consist of several tokens, like "Jr. III" in "Smith, Jr. III, John") + * @param givenName the first name of the author (may consist of several tokens, like "Charles Louis Xavier Joseph" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") + * @param givenNameAbbreviated the abbreviated first name of the author (may consist of several tokens, like "C. L. X. J." in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin"). It is a responsibility of the caller to create a reasonable abbreviation of the first name. + * @param namePrefix the von part of the author's name (may consist of several tokens, like "de la" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") + * @param familyName the last name of the author (may consist of several tokens, like "Vall{\'e}e Poussin" in "Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") + * @param nameSuffix the junior part of the author's name (may consist of several tokens, like "Jr. III" in "Smith, Jr. III, John") */ - public Author(String first, String firstabbr, String von, String last, String jr) { - firstPart = addDotIfAbbreviation(removeStartAndEndBraces(first)); - firstAbbr = removeStartAndEndBraces(firstabbr); - vonPart = removeStartAndEndBraces(von); - lastPart = removeStartAndEndBraces(last); - jrPart = removeStartAndEndBraces(jr); + public Author(String givenName, String givenNameAbbreviated, String namePrefix, String familyName, String nameSuffix) { + boolean keepBracesAtLastPart = StringUtil.isBlank(givenName) && StringUtil.isBlank(givenNameAbbreviated) && StringUtil.isBlank(namePrefix) && !StringUtil.isBlank(familyName) && StringUtil.isBlank(nameSuffix); + + this.givenName = addDotIfAbbreviation(removeStartAndEndBraces(givenName)); + this.givenNameAbbreviated = removeStartAndEndBraces(givenNameAbbreviated); + this.namePrefix = removeStartAndEndBraces(namePrefix); + if (keepBracesAtLastPart) { + // We do not remove braces here to keep institutions protected + // https://github.com/JabRef/jabref/issues/10031 + this.familyName = familyName; + } else { + this.familyName = removeStartAndEndBraces(familyName); + } + this.nameSuffix = removeStartAndEndBraces(nameSuffix); } public static String addDotIfAbbreviation(String name) { @@ -130,7 +140,7 @@ public static String addDotIfAbbreviation(String name) { @Override public int hashCode() { - return Objects.hash(firstAbbr, firstPart, jrPart, lastPart, vonPart); + return Objects.hash(givenNameAbbreviated, givenName, nameSuffix, familyName, namePrefix); } /** @@ -145,11 +155,11 @@ public boolean equals(Object other) { } if (other instanceof Author that) { - return Objects.equals(firstPart, that.firstPart) - && Objects.equals(firstAbbr, that.firstAbbr) - && Objects.equals(vonPart, that.vonPart) - && Objects.equals(lastPart, that.lastPart) - && Objects.equals(jrPart, that.jrPart); + return Objects.equals(givenName, that.givenName) + && Objects.equals(givenNameAbbreviated, that.givenNameAbbreviated) + && Objects.equals(namePrefix, that.namePrefix) + && Objects.equals(familyName, that.familyName) + && Objects.equals(nameSuffix, that.nameSuffix); } return false; @@ -183,7 +193,7 @@ private boolean properBrackets(String s) { } /** - * Removes start and end brace at a string + * Removes start and end brace both at the complete string and at beginning/end of a word *

* E.g., *

    @@ -248,8 +258,8 @@ private String removeStartAndEndBraces(String name) { * * @return first name of the author (may consist of several tokens) */ - public Optional getFirst() { - return Optional.ofNullable(firstPart); + public Optional getGivenName() { + return Optional.ofNullable(givenName); } /** @@ -257,17 +267,17 @@ public Optional getFirst() { * * @return abbreviated first name of the author (may consist of several tokens) */ - public Optional getFirstAbbr() { - return Optional.ofNullable(firstAbbr); + public Optional getGivenNameAbbreviated() { + return Optional.ofNullable(givenNameAbbreviated); } /** - * Returns the von part of the author's name stored in this object ("von"). + * Returns the von part of the author's name stored in this object ("von", "name prefix"). * * @return von part of the author's name (may consist of several tokens) */ - public Optional getVon() { - return Optional.ofNullable(vonPart); + public Optional getNamePrefix() { + return Optional.ofNullable(namePrefix); } /** @@ -275,17 +285,17 @@ public Optional getVon() { * * @return last name of the author (may consist of several tokens) */ - public Optional getLast() { - return Optional.ofNullable(lastPart); + public Optional getFamilyName() { + return Optional.ofNullable(familyName); } /** - * Returns the junior part of the author's name stored in this object ("Jr"). + * Returns the name suffix ("junior") part of the author's name stored in this object ("Jr"). * * @return junior part of the author's name (may consist of several tokens) or null if the author does not have a Jr. Part */ - public Optional getJr() { - return Optional.ofNullable(jrPart); + public Optional getNameSuffix() { + return Optional.ofNullable(nameSuffix); } /** @@ -293,11 +303,11 @@ public Optional getJr() { * * @return 'von Last' */ - public String getLastOnly() { - if (vonPart == null) { - return getLast().orElse(""); + public String getNamePrefixAndFamilyName() { + if (namePrefix == null) { + return getFamilyName().orElse(""); } else { - return lastPart == null ? vonPart : vonPart + ' ' + lastPart; + return familyName == null ? namePrefix : namePrefix + ' ' + familyName; } } @@ -307,13 +317,13 @@ public String getLastOnly() { * @param abbr true - abbreviate first name, false - do not abbreviate * @return 'von Last, Jr., First' (if abbr==false) or 'von Last, Jr., F.' (if abbr==true) */ - public String getLastFirst(boolean abbr) { - StringBuilder res = new StringBuilder(getLastOnly()); - getJr().ifPresent(jr -> res.append(", ").append(jr)); + public String getFamilyGiven(boolean abbr) { + StringBuilder res = new StringBuilder(getNamePrefixAndFamilyName()); + getNameSuffix().ifPresent(jr -> res.append(", ").append(jr)); if (abbr) { - getFirstAbbr().ifPresent(firstA -> res.append(", ").append(firstA)); + getGivenNameAbbreviated().ifPresent(firstA -> res.append(", ").append(firstA)); } else { - getFirst().ifPresent(first -> res.append(", ").append(first)); + getGivenName().ifPresent(first -> res.append(", ").append(first)); } return res.toString(); } @@ -324,26 +334,26 @@ public String getLastFirst(boolean abbr) { * @param abbr true - abbreviate first name, false - do not abbreviate * @return 'First von Last, Jr.' (if abbr==false) or 'F. von Last, Jr.' (if abbr==true) */ - public String getFirstLast(boolean abbr) { + public String getGivenFamily(boolean abbr) { StringBuilder res = new StringBuilder(); if (abbr) { - getFirstAbbr().map(firstA -> firstA + ' ').ifPresent(res::append); + getGivenNameAbbreviated().map(firstA -> firstA + ' ').ifPresent(res::append); } else { - getFirst().map(first -> first + ' ').ifPresent(res::append); + getGivenName().map(first -> first + ' ').ifPresent(res::append); } - res.append(getLastOnly()); - getJr().ifPresent(jr -> res.append(", ").append(jr)); + res.append(getNamePrefixAndFamilyName()); + getNameSuffix().ifPresent(jr -> res.append(", ").append(jr)); return res.toString(); } @Override public String toString() { final StringBuilder sb = new StringBuilder("Author{"); - sb.append("firstPart='").append(firstPart).append('\''); - sb.append(", firstAbbr='").append(firstAbbr).append('\''); - sb.append(", vonPart='").append(vonPart).append('\''); - sb.append(", lastPart='").append(lastPart).append('\''); - sb.append(", jrPart='").append(jrPart).append('\''); + sb.append("givenName='").append(givenName).append('\''); + sb.append(", givenNameAbbreviated='").append(givenNameAbbreviated).append('\''); + sb.append(", namePrefix='").append(namePrefix).append('\''); + sb.append(", familyName='").append(familyName).append('\''); + sb.append(", nameSuffix='").append(nameSuffix).append('\''); sb.append('}'); return sb.toString(); } @@ -355,9 +365,9 @@ public String toString() { */ public String getNameForAlphabetization() { StringBuilder res = new StringBuilder(); - getLast().ifPresent(res::append); - getJr().ifPresent(jr -> res.append(", ").append(jr)); - getFirstAbbr().ifPresent(firstA -> res.append(", ").append(firstA)); + getFamilyName().ifPresent(res::append); + getNameSuffix().ifPresent(jr -> res.append(", ").append(jr)); + getGivenNameAbbreviated().ifPresent(firstA -> res.append(", ").append(firstA)); while ((res.length() > 0) && (res.charAt(0) == '{')) { res.deleteCharAt(0); } @@ -369,12 +379,12 @@ public String getNameForAlphabetization() { */ public Author latexFree() { if (latexFreeAuthor == null) { - String first = getFirst().map(LatexToUnicodeAdapter::format).orElse(null); - String firstabbr = getFirstAbbr().map(LatexToUnicodeAdapter::format).orElse(null); - String von = getVon().map(LatexToUnicodeAdapter::format).orElse(null); - String last = getLast().map(LatexToUnicodeAdapter::format).orElse(null); - String jr = getJr().map(LatexToUnicodeAdapter::format).orElse(null); - latexFreeAuthor = new Author(first, firstabbr, von, last, jr); + String first = getGivenName().map(LatexToUnicodeAdapter::format).orElse(null); + String givenNameAbbreviated = getGivenNameAbbreviated().map(LatexToUnicodeAdapter::format).orElse(null); + String von = getNamePrefix().map(LatexToUnicodeAdapter::format).orElse(null); + String last = getFamilyName().map(LatexToUnicodeAdapter::format).orElse(null); + String jr = getNameSuffix().map(LatexToUnicodeAdapter::format).orElse(null); + latexFreeAuthor = new Author(first, givenNameAbbreviated, von, last, jr); latexFreeAuthor.latexFreeAuthor = latexFreeAuthor; } return latexFreeAuthor; diff --git a/src/main/java/org/jabref/model/entry/AuthorList.java b/src/main/java/org/jabref/model/entry/AuthorList.java index ab2888bfd09..17b68e02fbe 100644 --- a/src/main/java/org/jabref/model/entry/AuthorList.java +++ b/src/main/java/org/jabref/model/entry/AuthorList.java @@ -12,6 +12,8 @@ import org.jabref.architecture.AllowedToUseLogic; import org.jabref.logic.importer.AuthorListParser; +import org.jspecify.annotations.NonNull; + /** * This is an immutable class representing information of either author or editor field in bibtex record. *

    @@ -169,16 +171,11 @@ private static String andCoordinatedConjunction(List authors, boolean ox * @param authors The string of authors or editors in bibtex format to parse. * @return An AuthorList object representing the given authors. */ - public static AuthorList parse(final String authors) { - Objects.requireNonNull(authors); - - AuthorList authorList = AUTHOR_CACHE.get(authors); - if (authorList == null) { + public static AuthorList parse(@NonNull final String authors) { + return AUTHOR_CACHE.computeIfAbsent(authors, string -> { AuthorListParser parser = new AuthorListParser(); - authorList = parser.parse(authors); - AUTHOR_CACHE.put(authors, authorList); - } - return authorList; + return parser.parse(string); + }); } /** @@ -319,9 +316,9 @@ public String getAsNatbib() { var authors = getAuthors(); return switch (authors.size()) { case 0 -> ""; - case 1 -> authors.getFirst().getLastOnly(); - case 2 -> authors.getFirst().getLastOnly() + " and " + authors.get(1).getLastOnly(); - default -> authors.getFirst().getLastOnly() + " et al."; + case 1 -> authors.getFirst().getNamePrefixAndFamilyName(); + case 2 -> authors.getFirst().getNamePrefixAndFamilyName() + " and " + authors.get(1).getNamePrefixAndFamilyName(); + default -> authors.getFirst().getNamePrefixAndFamilyName() + " et al."; }; } @@ -341,7 +338,7 @@ public String getAsNatbib() { * Oxford comma. */ public String getAsLastNames(boolean oxfordComma) { - return andCoordinatedConjunction(getAuthors(), Author::getLastOnly, oxfordComma); + return andCoordinatedConjunction(getAuthors(), Author::getNamePrefixAndFamilyName, oxfordComma); } /** @@ -363,7 +360,7 @@ public String getAsLastNames(boolean oxfordComma) { * Oxford comma. */ public String getAsLastFirstNames(boolean abbreviate, boolean oxfordComma) { - return andCoordinatedConjunction(getAuthors(), auth -> auth.getLastFirst(abbreviate), oxfordComma); + return andCoordinatedConjunction(getAuthors(), auth -> auth.getFamilyGiven(abbreviate), oxfordComma); } @Override @@ -386,25 +383,25 @@ public String toString() { */ public String getAsLastFirstNamesWithAnd(boolean abbreviate) { return getAuthors().stream() - .map(author -> author.getLastFirst(abbreviate)) + .map(author -> author.getFamilyGiven(abbreviate)) .collect(Collectors.joining(" and ")); } /** - * Returns a list of authors separated with "and". The first author is formatted with {@link Author#getLastFirst(boolean)} and each subsequent author is formatted with {@link Author#getFirstLast(boolean)}. + * Returns a list of authors separated with "and". The first author is formatted with {@link Author#getFamilyGiven(boolean)} and each subsequent author is formatted with {@link Author#getGivenFamily(boolean)}. * * @param abbreviate first names. */ public String getAsLastFirstFirstLastNamesWithAnd(boolean abbreviate) { return switch (authors.size()) { case 0 -> ""; - case 1 -> authors.getFirst().getLastFirst(abbreviate); + case 1 -> authors.getFirst().getFamilyGiven(abbreviate); default -> authors.stream() .skip(1) - .map(author -> author.getFirstLast(abbreviate)) + .map(author -> author.getGivenFamily(abbreviate)) .collect(Collectors.joining( " and ", - authors.getFirst().getLastFirst(abbreviate) + " and ", + authors.getFirst().getFamilyGiven(abbreviate) + " and ", "")); }; } @@ -428,7 +425,7 @@ public String getAsLastFirstFirstLastNamesWithAnd(boolean abbreviate) { * Oxford comma. */ public String getAsFirstLastNames(boolean abbreviate, boolean oxfordComma) { - return andCoordinatedConjunction(getAuthors(), author -> author.getFirstLast(abbreviate), oxfordComma); + return andCoordinatedConjunction(getAuthors(), author -> author.getGivenFamily(abbreviate), oxfordComma); } /** @@ -469,7 +466,7 @@ public int hashCode() { */ public String getAsFirstLastNamesWithAnd() { return getAuthors().stream() - .map(author -> author.getFirstLast(false)) + .map(author -> author.getGivenFamily(false)) .collect(Collectors.joining(" and ")); } diff --git a/src/main/java/org/jabref/model/groups/LastNameGroup.java b/src/main/java/org/jabref/model/groups/LastNameGroup.java index 8fa6fff938c..7ee022af1b6 100644 --- a/src/main/java/org/jabref/model/groups/LastNameGroup.java +++ b/src/main/java/org/jabref/model/groups/LastNameGroup.java @@ -25,7 +25,7 @@ static List getAsLastNamesLatexFree(Field field, BibEntry bibEntry) { .map(AuthorList::latexFree) .map(AuthorList::getAuthors) .flatMap(Collection::stream) - .map(Author::getLast) + .map(Author::getFamilyName) .flatMap(Optional::stream) .collect(Collectors.toList()); } diff --git a/src/test/java/org/jabref/logic/exporter/MSBibExportFormatFilesTest.java b/src/test/java/org/jabref/logic/exporter/MSBibExportFormatFilesTest.java index e3cc7deb7d0..6a03d17cd3c 100644 --- a/src/test/java/org/jabref/logic/exporter/MSBibExportFormatFilesTest.java +++ b/src/test/java/org/jabref/logic/exporter/MSBibExportFormatFilesTest.java @@ -7,7 +7,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.jabref.logic.importer.ImportFormatPreferences; @@ -44,8 +43,8 @@ static Stream fileNames() throws IOException, URISyntaxException { return stream.map(n -> n.getFileName().toString()) .filter(n -> n.endsWith(".bib")) .filter(n -> n.startsWith("MsBib")) - .collect(Collectors.toList()) - .stream(); + // mapping required, because we get "source already consumed or closed" otherwise + .toList().stream(); } } @@ -75,7 +74,7 @@ void performExport(String filename) throws IOException, SaveException { String expected = String.join("\n", Files.readAllLines(expectedFile)); String actual = String.join("\n", Files.readAllLines(exportedFile)); - // The order of elements changes from Windows to Travis environment somehow + // The order of the XML elements changes from Windows to Travis environment somehow // The order does not really matter, so we ignore it. // Source: https://stackoverflow.com/a/16540679/873282 assertThat(expected, isSimilarTo(actual) diff --git a/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java b/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java index 715e532b781..2c8f81058a2 100644 --- a/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java +++ b/src/test/java/org/jabref/logic/formatter/bibtexfields/NormalizeNamesFormatterTest.java @@ -19,6 +19,9 @@ public void setUp() { @Test public void normalizeAuthorList() { + assertEquals("{Society of Automotive Engineers}", formatter.format("{Society of Automotive Engineers}")); + assertEquals("{Company Name, LLC}", formatter.format("{Company Name, LLC}")); + assertEquals("Bilbo, Staci D.", formatter.format("Staci D Bilbo")); assertEquals("Bilbo, Staci D.", formatter.format("Staci D. Bilbo")); diff --git a/src/test/java/org/jabref/logic/importer/AuthorListParserTest.java b/src/test/java/org/jabref/logic/importer/AuthorListParserTest.java index 42a3c18b378..2943fc5fec6 100644 --- a/src/test/java/org/jabref/logic/importer/AuthorListParserTest.java +++ b/src/test/java/org/jabref/logic/importer/AuthorListParserTest.java @@ -20,7 +20,7 @@ private static Stream parseSingleAuthorCorrectly() { Arguments.of("王, 军", new Author("军", "军.", null, "王", null)), Arguments.of("Doe, John", new Author("John", "J.", null, "Doe", null)), Arguments.of("von Berlichingen zu Hornberg, Johann Gottfried", new Author("Johann Gottfried", "J. G.", "von", "Berlichingen zu Hornberg", null)), - Arguments.of("{Robert and Sons, Inc.}", new Author(null, null, null, "Robert and Sons, Inc.", null)), + Arguments.of("{Robert and Sons, Inc.}", new Author(null, null, null, "{Robert and Sons, Inc.}", null)), Arguments.of("al-Ṣāliḥ, Abdallāh", new Author("Abdallāh", "A.", null, "al-Ṣāliḥ", null)), Arguments.of("de la Vallée Poussin, Jean Charles Gabriel", new Author("Jean Charles Gabriel", "J. C. G.", "de la", "Vallée Poussin", null)), Arguments.of("de la Vallée Poussin, J. C. G.", new Author("J. C. G.", "J. C. G.", "de la", "Vallée Poussin", null)), @@ -28,7 +28,12 @@ private static Stream parseSingleAuthorCorrectly() { Arguments.of("Uhlenhaut, N Henriette", new Author("N Henriette", "N. H.", null, "Uhlenhaut", null)), Arguments.of("Nu{\\~{n}}ez, Jose", new Author("Jose", "J.", null, "Nu{\\~{n}}ez", null)), // parseAuthorWithFirstNameAbbreviationContainingUmlaut - Arguments.of("{\\OE}rjan Umlauts", new Author("{\\OE}rjan", "{\\OE}.", null, "Umlauts", null)) + Arguments.of("{\\OE}rjan Umlauts", new Author("{\\OE}rjan", "{\\OE}.", null, "Umlauts", null)), + Arguments.of("{Company Name, LLC}", new Author("", "", null, "{Company Name, LLC}", null)), + Arguments.of("{Society of Automotive Engineers}", new Author("", "", null, "{Society of Automotive Engineers}", null)), + + // Demonstrate the "von" part parsing of a non-braced name + Arguments.of("Society of Automotive Engineers", new Author("Society", "S.", "of", "Automotive Engineers", null)) ); } diff --git a/src/test/java/org/jabref/logic/msbib/MSBibConverterTest.java b/src/test/java/org/jabref/logic/msbib/MSBibConverterTest.java index db4d1b305a6..c254cbcc478 100644 --- a/src/test/java/org/jabref/logic/msbib/MSBibConverterTest.java +++ b/src/test/java/org/jabref/logic/msbib/MSBibConverterTest.java @@ -1,7 +1,5 @@ package org.jabref.logic.msbib; -import java.util.Optional; - import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; @@ -30,13 +28,9 @@ class MSBibConverterTest { .withField(StandardField.TITLE, "Overcoming Open Source Project Entry Barriers with a Portal for Newcomers") .withField(StandardField.FILE, ":https\\://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=7886910:PDF"); - private BibEntry entry; - @Test void convert() { - entry = BIB_ENTRY_TEST; - MSBibConverter.convert(entry); - - assertEquals(Optional.of("english"), entry.getField(StandardField.LANGUAGE)); + MSBibEntry result = MSBibConverter.convert(BIB_ENTRY_TEST); + assertEquals("1033", result.fields.get("LCID")); } } diff --git a/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java b/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java index 703a74d0201..cbf128bf6da 100644 --- a/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java +++ b/src/test/java/org/jabref/logic/msbib/MsBibAuthorTest.java @@ -10,7 +10,7 @@ public class MsBibAuthorTest { @Test - public void getFirstName() { + public void getGivenNameName() { Author author = new Author("Gustav Peter Johann", null, null, "Bach", null); MsBibAuthor msBibAuthor = new MsBibAuthor(author); assertEquals("Gustav", msBibAuthor.getFirstName()); @@ -38,14 +38,14 @@ public void getNoFirstName() { } @Test - public void getLastName() { + public void getFamilyNameName() { Author author = new Author("Gustav Peter Johann", null, null, "Bach", null); MsBibAuthor msBibAuthor = new MsBibAuthor(author); assertEquals("Bach", msBibAuthor.getLastName()); } @Test - public void getVonAndLastName() { + public void getNamePrefixAndLastName() { Author author = new Author("John", null, "von", "Neumann", null); MsBibAuthor msBibAuthor = new MsBibAuthor(author); assertEquals("von Neumann", msBibAuthor.getLastName()); diff --git a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java index 0f8ca79b160..109c857f7f1 100644 --- a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java +++ b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTest.java @@ -372,19 +372,15 @@ void institutionAuthorMarker() throws IOException { abbreviationRepository); Map entryDBMap = new HashMap<>(); - List entries = new ArrayList<>(); - BibDatabase database = new BibDatabase(); - BibEntry entry = new BibEntry(); - entry.setCitationKey("JabRef2016"); - entry.setType(StandardEntryType.Article); - entry.setField(StandardField.AUTHOR, "{JabRef Development Team}"); - entry.setField(StandardField.TITLE, "JabRef Manual"); - entry.setField(StandardField.YEAR, "2016"); - database.insertEntry(entry); - entries.add(entry); + BibEntry entry = new BibEntry(StandardEntryType.Article) + .withCitationKey("JabRef2016") + .withField(StandardField.AUTHOR, "{JabRef Development Team}") + .withField(StandardField.TITLE, "JabRef Manual") + .withField(StandardField.YEAR, "2016"); + List entries = List.of(entry); + BibDatabase database = new BibDatabase(entries); entryDBMap.put(entry, database); - assertEquals("[JabRef Development Team, 2016]", getCitationMarker2(style, entries, entryDBMap, true, null, null, null)); diff --git a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java index 0a6fdeea6e6..804056b031b 100644 --- a/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java +++ b/src/test/java/org/jabref/logic/openoffice/style/OOBibStyleTestHelper.java @@ -153,8 +153,8 @@ static CitationMarkerEntry makeCitationMarkerEntry(BibEntry entry, return result; } - /* - * Similar to old API. pageInfo is new, and unlimAuthors is + /** + * @implNote Similar to old API. pageInfo is new, and unlimAuthors is * replaced with isFirstAppearanceOfSource */ static String getCitationMarker2ab(OOBibStyle style, @@ -177,7 +177,7 @@ static String getCitationMarker2ab(OOBibStyle style, isFirstAppearanceOfSource = new Boolean[entries.size()]; Arrays.fill(isFirstAppearanceOfSource, false); } - List citationMarkerEntries = new ArrayList<>(); + List citationMarkerEntries = new ArrayList<>(entries.size()); for (int i = 0; i < entries.size(); i++) { BibEntry entry = entries.get(i); CitationMarkerEntry e = makeCitationMarkerEntry(entry, diff --git a/src/test/java/org/jabref/logic/openoffice/style/OOPreFormatterTest.java b/src/test/java/org/jabref/logic/openoffice/style/OOPreFormatterTest.java index 3c4d828dbec..790483fd57c 100644 --- a/src/test/java/org/jabref/logic/openoffice/style/OOPreFormatterTest.java +++ b/src/test/java/org/jabref/logic/openoffice/style/OOPreFormatterTest.java @@ -14,6 +14,11 @@ public void plainFormat() { assertEquals("\\", new OOPreFormatter().format("\\\\")); } + @Test + public void removeBraces() { + assertEquals("aaa", new OOPreFormatter().format("{aaa}")); + } + @Test public void formatAccents() { assertEquals("ä", new OOPreFormatter().format("{\\\"{a}}")); diff --git a/src/test/java/org/jabref/model/entry/AuthorListTest.java b/src/test/java/org/jabref/model/entry/AuthorListTest.java index 11fd5a8e35f..6f49c711bae 100644 --- a/src/test/java/org/jabref/model/entry/AuthorListTest.java +++ b/src/test/java/org/jabref/model/entry/AuthorListTest.java @@ -734,50 +734,50 @@ public void getEmptyAuthor() { @Test public void getAuthor() { Author author = AuthorList.parse("John Smith and von Neumann, Jr, John").getAuthor(0); - assertEquals(Optional.of("John"), author.getFirst()); - assertEquals(Optional.of("J."), author.getFirstAbbr()); - assertEquals("John Smith", author.getFirstLast(false)); - assertEquals("J. Smith", author.getFirstLast(true)); - assertEquals(Optional.empty(), author.getJr()); - assertEquals(Optional.of("Smith"), author.getLast()); - assertEquals("Smith, John", author.getLastFirst(false)); - assertEquals("Smith, J.", author.getLastFirst(true)); - assertEquals("Smith", author.getLastOnly()); + assertEquals(Optional.of("John"), author.getGivenName()); + assertEquals(Optional.of("J."), author.getGivenNameAbbreviated()); + assertEquals("John Smith", author.getGivenFamily(false)); + assertEquals("J. Smith", author.getGivenFamily(true)); + assertEquals(Optional.empty(), author.getNameSuffix()); + assertEquals(Optional.of("Smith"), author.getFamilyName()); + assertEquals("Smith, John", author.getFamilyGiven(false)); + assertEquals("Smith, J.", author.getFamilyGiven(true)); + assertEquals("Smith", author.getNamePrefixAndFamilyName()); assertEquals("Smith, J.", author.getNameForAlphabetization()); - assertEquals(Optional.empty(), author.getVon()); + assertEquals(Optional.empty(), author.getNamePrefix()); author = AuthorList.parse("Peter Black Brown").getAuthor(0); - assertEquals(Optional.of("Peter Black"), author.getFirst()); - assertEquals(Optional.of("P. B."), author.getFirstAbbr()); - assertEquals("Peter Black Brown", author.getFirstLast(false)); - assertEquals("P. B. Brown", author.getFirstLast(true)); - assertEquals(Optional.empty(), author.getJr()); - assertEquals(Optional.empty(), author.getVon()); + assertEquals(Optional.of("Peter Black"), author.getGivenName()); + assertEquals(Optional.of("P. B."), author.getGivenNameAbbreviated()); + assertEquals("Peter Black Brown", author.getGivenFamily(false)); + assertEquals("P. B. Brown", author.getGivenFamily(true)); + assertEquals(Optional.empty(), author.getNameSuffix()); + assertEquals(Optional.empty(), author.getNamePrefix()); author = AuthorList.parse("John Smith and von Neumann, Jr, John").getAuthor(1); - assertEquals(Optional.of("John"), author.getFirst()); - assertEquals(Optional.of("J."), author.getFirstAbbr()); - assertEquals("John von Neumann, Jr", author.getFirstLast(false)); - assertEquals("J. von Neumann, Jr", author.getFirstLast(true)); - assertEquals(Optional.of("Jr"), author.getJr()); - assertEquals(Optional.of("Neumann"), author.getLast()); - assertEquals("von Neumann, Jr, John", author.getLastFirst(false)); - assertEquals("von Neumann, Jr, J.", author.getLastFirst(true)); - assertEquals("von Neumann", author.getLastOnly()); + assertEquals(Optional.of("John"), author.getGivenName()); + assertEquals(Optional.of("J."), author.getGivenNameAbbreviated()); + assertEquals("John von Neumann, Jr", author.getGivenFamily(false)); + assertEquals("J. von Neumann, Jr", author.getGivenFamily(true)); + assertEquals(Optional.of("Jr"), author.getNameSuffix()); + assertEquals(Optional.of("Neumann"), author.getFamilyName()); + assertEquals("von Neumann, Jr, John", author.getFamilyGiven(false)); + assertEquals("von Neumann, Jr, J.", author.getFamilyGiven(true)); + assertEquals("von Neumann", author.getNamePrefixAndFamilyName()); assertEquals("Neumann, Jr, J.", author.getNameForAlphabetization()); - assertEquals(Optional.of("von"), author.getVon()); + assertEquals(Optional.of("von"), author.getNamePrefix()); } @Test public void companyAuthor() { Author author = AuthorList.parse("{JabRef Developers}").getAuthor(0); - Author expected = new Author(null, null, null, "JabRef Developers", null); + Author expected = new Author(null, null, null, "{JabRef Developers}", null); assertEquals(expected, author); } @Test public void companyAuthorAndPerson() { - Author company = new Author(null, null, null, "JabRef Developers", null); + Author company = new Author(null, null, null, "{JabRef Developers}", null); Author person = new Author("Stefan", "S.", null, "Kolb", null); assertEquals(Arrays.asList(company, person), AuthorList.parse("{JabRef Developers} and Stefan Kolb").getAuthors()); } @@ -785,7 +785,7 @@ public void companyAuthorAndPerson() { @Test public void companyAuthorWithLowerCaseWord() { Author author = AuthorList.parse("{JabRef Developers on Fire}").getAuthor(0); - Author expected = new Author(null, null, null, "JabRef Developers on Fire", null); + Author expected = new Author(null, null, null, "{JabRef Developers on Fire}", null); assertEquals(expected, author); } @@ -985,17 +985,17 @@ public void getAuthorsForAlphabetization() { @Test public void removeStartAndEndBraces() { assertEquals("{A}bbb{c}", AuthorList.parse("{A}bbb{c}").getAsLastNames(false)); - assertEquals("Vall{\\'e}e Poussin", AuthorList.parse("{Vall{\\'e}e Poussin}").getAsLastNames(false)); + assertEquals("{Vall{\\'e}e Poussin}", AuthorList.parse("{Vall{\\'e}e Poussin}").getAsLastNames(false)); assertEquals("Poussin", AuthorList.parse("{Vall{\\'e}e} {Poussin}").getAsLastNames(false)); assertEquals("Poussin", AuthorList.parse("Vall{\\'e}e Poussin").getAsLastNames(false)); assertEquals("Lastname", AuthorList.parse("Firstname {Lastname}").getAsLastNames(false)); - assertEquals("Firstname Lastname", AuthorList.parse("{Firstname Lastname}").getAsLastNames(false)); + assertEquals("{Firstname Lastname}", AuthorList.parse("{Firstname Lastname}").getAsLastNames(false)); } @Test public void createCorrectInitials() { assertEquals(Optional.of("J. G."), - AuthorList.parse("Hornberg, Johann Gottfried").getAuthor(0).getFirstAbbr()); + AuthorList.parse("Hornberg, Johann Gottfried").getAuthor(0).getGivenNameAbbreviated()); } @Test @@ -1052,34 +1052,34 @@ public void parseNameWithBraces() throws Exception { public void parseFirstNameFromFirstAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("Mu{\\d{h}}ammad", AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") - .getAuthor(0).getFirst().orElse(null)); + .getAuthor(0).getGivenName().orElse(null)); } @Test public void parseFirstNameFromSecondAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("Corrado", AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") - .getAuthor(1).getFirst().orElse(null)); + .getAuthor(1).getGivenName().orElse(null)); } @Test public void parseLastNameFromFirstAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("al-Khw{\\={a}}rizm{\\={i}}", AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") - .getAuthor(0).getLast().orElse(null)); + .getAuthor(0).getFamilyName().orElse(null)); } @Test public void parseLastNameFromSecondAuthorMultipleAuthorsWithLatexNames() throws Exception { assertEquals("B{\\\"o}hm", AuthorList.parse("Mu{\\d{h}}ammad al-Khw{\\={a}}rizm{\\={i}} and Corrado B{\\\"o}hm") - .getAuthor(1).getLast().orElse(null)); + .getAuthor(1).getFamilyName().orElse(null)); } @Test public void parseInstitutionAuthorWithLatexNames() throws Exception { - assertEquals("The Ban\\={u} M\\={u}s\\={a} brothers", - AuthorList.parse("{The Ban\\={u} M\\={u}s\\={a} brothers}").getAuthor(0).getLast().orElse(null)); + assertEquals("{The Ban\\={u} M\\={u}s\\={a} brothers}", + AuthorList.parse("{The Ban\\={u} M\\={u}s\\={a} brothers}").getAuthor(0).getFamilyName().orElse(null)); } @Test diff --git a/src/test/java/org/jabref/model/entry/AuthorTest.java b/src/test/java/org/jabref/model/entry/AuthorTest.java index a1176bf2ac6..592f84f9923 100644 --- a/src/test/java/org/jabref/model/entry/AuthorTest.java +++ b/src/test/java/org/jabref/model/entry/AuthorTest.java @@ -1,5 +1,7 @@ package org.jabref.model.entry; +import java.util.Optional; + import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; @@ -70,6 +72,11 @@ void addDotIfAbbreviationEndsWithDoubleAbbreviation() { assertEquals("Ameli A. A.", Author.addDotIfAbbreviation("Ameli AA")); } + @Test + void bracesKept() { + assertEquals(Optional.of("{Company Name, LLC}"), new Author("", "", null, "{Company Name, LLC}", null).getFamilyName()); + } + @ParameterizedTest @ValueSource(strings = {"1", "1 23"}) void addDotIfAbbreviationIfStartsWithNumber(String input) {