diff --git a/CHANGELOG.md b/CHANGELOG.md index 50f743ca5e8..4db46a1e94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where the entry editor context menu was not shown correctly when JabRef is opened on a second, extended screen [#11323](https://github.com/JabRef/jabref/issues/11323), [#11174](https://github.com/JabRef/jabref/issues/11174) - We fixed an issue where the value of "Override default font settings" was not applied on startup [#11344](https://github.com/JabRef/jabref/issues/11344) - We fixed an issue when "Library changed on disk" appeared after a save by JabRef. [#4877](https://github.com/JabRef/jabref/issues/4877) +- We fixed an issue where the Pubmed/Medline Plain importer would not respect the user defined keyword separator [#11413](https://github.com/JabRef/jabref/issues/11413) - We fixed an issue where the value of "Override default font settings" was not applied on startup [#11344](https://github.com/JabRef/jabref/issues/11344) - We fixed an issue where DatabaseChangeDetailsView was not scrollable when reviewing external metadata changes [#11220](https://github.com/JabRef/jabref/issues/11220) diff --git a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java index df54b8cf52f..8aeb145ab42 100644 --- a/src/main/java/org/jabref/logic/importer/ImportFormatReader.java +++ b/src/main/java/org/jabref/logic/importer/ImportFormatReader.java @@ -70,7 +70,7 @@ public void reset() { formats.add(new InspecImporter()); formats.add(new IsiImporter()); formats.add(new MedlineImporter()); - formats.add(new MedlinePlainImporter()); + formats.add(new MedlinePlainImporter(importFormatPreferences)); formats.add(new ModsImporter(importFormatPreferences)); formats.add(new MsBibImporter()); formats.add(new OvidImporter()); diff --git a/src/main/java/org/jabref/logic/importer/fileformat/MedlinePlainImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/MedlinePlainImporter.java index 1a10817f0d1..c66aef80d45 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/MedlinePlainImporter.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/MedlinePlainImporter.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.regex.Pattern; +import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.Importer; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.util.StandardFileType; @@ -32,6 +33,11 @@ public class MedlinePlainImporter extends Importer { private static final Pattern PMCR_PATTERN = Pattern.compile("PMCR.*-.*"); private static final Pattern CREATE_DATE_PATTERN = Pattern.compile("\\d{4}/[0123]?\\d/\\s?[012]\\d:[0-5]\\d"); private static final Pattern COMPLETE_DATE_PATTERN = Pattern.compile("\\d{8}"); + private final ImportFormatPreferences importFormatPreferences; + + public MedlinePlainImporter(ImportFormatPreferences importFormatPreferences) { + this.importFormatPreferences = importFormatPreferences; + } @Override public String getName() { @@ -84,9 +90,9 @@ public ParserResult importDatabase(BufferedReader reader) throws IOException { } EntryType type = BibEntry.DEFAULT_TYPE; - String author = ""; - String editor = ""; - String comment = ""; + StringBuilder author = new StringBuilder(); + StringBuilder editor = new StringBuilder(); + StringBuilder comment = new StringBuilder(); Map fieldConversionMap = new HashMap<>(); String[] lines = entry1.split("\n"); @@ -101,7 +107,7 @@ public ParserResult importDatabase(BufferedReader reader) throws IOException { continue; } if (lines[j + 1].charAt(4) != '-') { - if ((current.length() > 0) && !Character.isWhitespace(current.charAt(current.length() - 1))) { + if ((!current.isEmpty()) && !Character.isWhitespace(current.charAt(current.length() - 1))) { current.append(' '); } current.append(lines[j + 1].trim()); @@ -128,16 +134,16 @@ public ParserResult importDatabase(BufferedReader reader) throws IOException { addStandardNumber(fieldConversionMap, label, value); if ("FAU".equals(label)) { - if ("".equals(author)) { - author = value; + if (author.isEmpty()) { + author = new StringBuilder(value); } else { - author += " and " + value; + author.append(" and ").append(value); } } else if ("FED".equals(label)) { - if ("".equals(editor)) { - editor = value; + if (editor.isEmpty()) { + editor = new StringBuilder(value); } else { - editor += " and " + value; + editor.append(" and ").append(value); } } @@ -176,34 +182,47 @@ public ParserResult importDatabase(BufferedReader reader) throws IOException { } } - if ("IRAD".equals(label) || "IR".equals(label) || "FIR".equals(label)) { - String oldInvestigator = fieldConversionMap.get(new UnknownField("investigator")); - if (oldInvestigator == null) { - fieldConversionMap.put(new UnknownField("investigator"), value); - } else { - fieldConversionMap.put(new UnknownField("investigator"), oldInvestigator + ", " + value); + switch (label) { + case "IRAD", + "IR", + "FIR" -> { + fieldConversionMap.merge(new UnknownField("investigator"), value, (a, b) -> a + ", " + b); } - } else if ("MH".equals(label) || "OT".equals(label)) { - if (!fieldConversionMap.containsKey(StandardField.KEYWORDS)) { - fieldConversionMap.put(StandardField.KEYWORDS, value); - } else { - String kw = fieldConversionMap.get(StandardField.KEYWORDS); - fieldConversionMap.put(StandardField.KEYWORDS, kw + ", " + value); + case "MH", + "OT" -> { + if (!fieldConversionMap.containsKey(StandardField.KEYWORDS)) { + fieldConversionMap.put(StandardField.KEYWORDS, value); + } else { + fieldConversionMap.compute(StandardField.KEYWORDS, (k, kw) -> kw + importFormatPreferences.bibEntryPreferences().getKeywordSeparator() + " " + value); + } } - } else if ("CON".equals(label) || "CIN".equals(label) || "EIN".equals(label) || "EFR".equals(label) - || "CRI".equals(label) || "CRF".equals(label) || "PRIN".equals(label) || "PROF".equals(label) - || "RPI".equals(label) || "RPF".equals(label) || "RIN".equals(label) || "ROF".equals(label) - || "UIN".equals(label) || "UOF".equals(label) || "SPIN".equals(label) || "ORI".equals(label)) { - if (!comment.isEmpty()) { - comment = comment + "\n"; + case "CON", + "CIN", + "EIN", + "EFR", + "CRI", + "CRF", + "PRIN", + "PROF", + "RPI", + "RPF", + "RIN", + "ROF", + "UIN", + "UOF", + "SPIN", + "ORI" -> { + if (!comment.isEmpty()) { + comment.append("\n"); + } + comment.append(value); } - comment = comment + value; } } - fixAuthors(fieldConversionMap, author, StandardField.AUTHOR); - fixAuthors(fieldConversionMap, editor, StandardField.EDITOR); + fixAuthors(fieldConversionMap, author.toString(), StandardField.AUTHOR); + fixAuthors(fieldConversionMap, editor.toString(), StandardField.EDITOR); if (!comment.isEmpty()) { - fieldConversionMap.put(StandardField.COMMENT, comment); + fieldConversionMap.put(StandardField.COMMENT, comment.toString()); } BibEntry b = new BibEntry(type); @@ -222,29 +241,29 @@ private boolean checkLineValidity(String line) { private EntryType addSourceType(String value, EntryType type) { String val = value.toLowerCase(Locale.ENGLISH); - switch (val) { - case "book": - return StandardEntryType.Book; - case "journal article": - case "classical article": - case "corrected and republished article": - case "historical article": - case "introductory journal article": - case "newspaper article": - return StandardEntryType.Article; - case "clinical conference": - case "consensus development conference": - case "consensus development conference, nih": - return StandardEntryType.Conference; - case "technical report": - return StandardEntryType.TechReport; - case "editorial": - return StandardEntryType.InProceedings; - case "overall": - return StandardEntryType.Proceedings; - default: - return type; - } + return switch (val) { + case "book" -> + StandardEntryType.Book; + case "journal article", + "classical article", + "corrected and republished article", + "historical article", + "introductory journal article", + "newspaper article" -> + StandardEntryType.Article; + case "clinical conference", + "consensus development conference", + "consensus development conference, nih" -> + StandardEntryType.Conference; + case "technical report" -> + StandardEntryType.TechReport; + case "editorial" -> + StandardEntryType.InProceedings; + case "overall" -> + StandardEntryType.Proceedings; + default -> + type; + }; } private void addStandardNumber(Map hm, String lab, String value) { @@ -333,7 +352,7 @@ private void addTitles(Map hm, String lab, String val, EntryType } private void addAbstract(Map hm, String lab, String value) { - String abstractValue = ""; + String abstractValue; if ("AB".equals(lab)) { // adds copyright information that comes at the end of an abstract if (value.contains("Copyright")) { @@ -345,12 +364,7 @@ private void addAbstract(Map hm, String lab, String value) { } else { abstractValue = value; } - String oldAb = hm.get(StandardField.ABSTRACT); - if (oldAb == null) { - hm.put(StandardField.ABSTRACT, abstractValue); - } else { - hm.put(StandardField.ABSTRACT, oldAb + '\n' + abstractValue); - } + hm.merge(StandardField.ABSTRACT, abstractValue, (a, b) -> a + '\n' + b); } else if ("OAB".equals(lab) || "OABL".equals(lab)) { hm.put(new UnknownField("other-abstract"), value); } diff --git a/src/main/java/org/jabref/model/entry/field/StandardField.java b/src/main/java/org/jabref/model/entry/field/StandardField.java index 776e5019054..842883ff682 100644 --- a/src/main/java/org/jabref/model/entry/field/StandardField.java +++ b/src/main/java/org/jabref/model/entry/field/StandardField.java @@ -11,7 +11,7 @@ /** * Standard BibTeX and BibLaTeX fields, as well as "normal" JabRef specific fields. - * + *

* See {@link FieldNameLabel#getDescription(org.jabref.model.entry.field.Field)} for a description of each field. */ public enum StandardField implements Field { @@ -141,7 +141,7 @@ public enum StandardField implements Field { CREATIONDATE("creationdate", FieldProperty.DATE), MODIFICATIONDATE("modificationdate", FieldProperty.DATE); - public static Set AUTOMATIC_FIELDS = Set.of(OWNER, TIMESTAMP, CREATIONDATE, MODIFICATIONDATE); + public static final Set AUTOMATIC_FIELDS = Set.of(OWNER, TIMESTAMP, CREATIONDATE, MODIFICATIONDATE); private static final Map NAME_TO_STANDARD_FIELD = new HashMap<>(); diff --git a/src/test/java/org/jabref/logic/importer/ImporterTest.java b/src/test/java/org/jabref/logic/importer/ImporterTest.java index 4db39bf1635..706a66aac3d 100644 --- a/src/test/java/org/jabref/logic/importer/ImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/ImporterTest.java @@ -117,7 +117,7 @@ public static Stream instancesToTest() { new InspecImporter(), new IsiImporter(), new MedlineImporter(), - new MedlinePlainImporter(), + new MedlinePlainImporter(importFormatPreferences), new ModsImporter(importFormatPreferences), new MsBibImporter(), new OvidImporter(), diff --git a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java index 22445e563ce..f0ff769ee1c 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/BibtexParserTest.java @@ -829,7 +829,7 @@ void parseRecognizesMultipleStrings() throws IOException { @Test void parseRecognizesStringAndEntry() throws IOException { ParserResult result = parser.parse( - new StringReader("" + "@string{bourdieu = {Bourdieu, Pierre}}" + new StringReader("@string{bourdieu = {Bourdieu, Pierre}}" + "@book{bourdieu-2002-questions-sociologie, " + " Address = {Paris}," + " Author = bourdieu," + " Isbn = 2707318256," + " Publisher = {Minuit}," + " Title = {Questions de sociologie}," + " Year = 2002" + "}")); @@ -954,7 +954,14 @@ void parsKeesNewlines() throws IOException { @Test void parsKeepsMultipleNewlines() throws IOException { ParserResult result = parser - .parse(new StringReader("@article{test,a = {a\n\nb}," + "b = {a\n \nb}," + "c = {a \n \n b}}")); + .parse(new StringReader(""" + @article{test,a = {a + + b},b = {a + \s + b},c = {a\s + \s + b}}""")); Collection parsedEntries = result.getDatabase().getEntries(); BibEntry parsedEntry = parsedEntries.iterator().next(); @@ -1247,26 +1254,36 @@ void parserKeepsSaveActions() throws IOException { @Test void parseRecognizesCRLFLineBreak() throws IOException { ParserResult result = parser.parse( - new StringReader("@InProceedings{6055279,\r\n" + " Title = {Educational session 1},\r\n" - + " Booktitle = {Custom Integrated Circuits Conference (CICC), 2011 IEEE},\r\n" - + " Year = {2011},\r\n" + " Month = {Sept},\r\n" - + " Pages = {1-7},\r\n" - + " Abstract = {Start of the above-titled section of the conference proceedings record.},\r\n" - + " DOI = {10.1109/CICC.2011.6055279},\r\n" - + " ISSN = {0886-5930}\r\n" + "}\r\n")); + new StringReader(""" + @InProceedings{6055279,\r + Title = {Educational session 1},\r + Booktitle = {Custom Integrated Circuits Conference (CICC), 2011 IEEE},\r + Year = {2011},\r + Month = {Sept},\r + Pages = {1-7},\r + Abstract = {Start of the above-titled section of the conference proceedings record.},\r + DOI = {10.1109/CICC.2011.6055279},\r + ISSN = {0886-5930}\r + }\r + """)); assertEquals("\r\n", result.getDatabase().getNewLineSeparator()); } @Test void parseRecognizesLFLineBreak() throws IOException { ParserResult result = parser.parse( - new StringReader("@InProceedings{6055279,\n" + " Title = {Educational session 1},\n" - + " Booktitle = {Custom Integrated Circuits Conference (CICC), 2011 IEEE},\n" - + " Year = {2011},\n" + " Month = {Sept},\n" - + " Pages = {1-7},\n" - + " Abstract = {Start of the above-titled section of the conference proceedings record.},\n" - + " DOI = {10.1109/CICC.2011.6055279},\n" - + " ISSN = {0886-5930}\n" + "}\n")); + new StringReader(""" + @InProceedings{6055279, + Title = {Educational session 1}, + Booktitle = {Custom Integrated Circuits Conference (CICC), 2011 IEEE}, + Year = {2011}, + Month = {Sept}, + Pages = {1-7}, + Abstract = {Start of the above-titled section of the conference proceedings record.}, + DOI = {10.1109/CICC.2011.6055279}, + ISSN = {0886-5930} + } + """)); assertEquals("\n", result.getDatabase().getNewLineSeparator()); } @@ -1343,7 +1360,7 @@ void integrationTestBiblatexMode() throws IOException { } @Test - void integrationTestGroupTree() throws IOException, ParseException { + void integrationTestGroupTree() throws IOException { ParserResult result = parser.parse(new StringReader(""" @comment{jabref-meta: groupsversion:3;} @comment{jabref-meta: groupstree: @@ -1756,12 +1773,11 @@ void parseOtherTypeTest() throws Exception { @Test void parseRecognizesDatabaseID() throws Exception { String expectedDatabaseID = "q1w2e3r4t5z6"; - StringBuilder sharedDatabaseFileContent = new StringBuilder() - .append("\\% DBID: ").append(expectedDatabaseID) - .append(OS.NEWLINE) - .append("@Article{a}"); + String sharedDatabaseFileContent = "\\% DBID: " + expectedDatabaseID + + OS.NEWLINE + + "@Article{a}"; - ParserResult parserResult = parser.parse(new StringReader(sharedDatabaseFileContent.toString())); + ParserResult parserResult = parser.parse(new StringReader(sharedDatabaseFileContent)); String actualDatabaseID = parserResult.getDatabase().getSharedDatabaseID().get(); assertEquals(expectedDatabaseID, actualDatabaseID); @@ -1769,12 +1785,11 @@ void parseRecognizesDatabaseID() throws Exception { @Test void parseDoesNotRecognizeDatabaseIDasUserComment() throws Exception { - StringBuilder sharedDatabaseFileContent = new StringBuilder() - .append("\\% Encoding: UTF-8").append(OS.NEWLINE) - .append("\\% DBID: q1w2e3r4t5z6").append(OS.NEWLINE) - .append("@Article{a}"); + String sharedDatabaseFileContent = "\\% Encoding: UTF-8" + OS.NEWLINE + + "\\% DBID: q1w2e3r4t5z6" + OS.NEWLINE + + "@Article{a}"; - ParserResult parserResult = parser.parse(new StringReader(sharedDatabaseFileContent.toString())); + ParserResult parserResult = parser.parse(new StringReader(sharedDatabaseFileContent)); List entries = parserResult.getDatabase().getEntries(); assertEquals(1, entries.size()); @@ -2121,10 +2136,11 @@ void bibTeXConstantAprilIsParsedAsStringMonthAprilWhenReadingTheField() throws P @Test void parseDuplicateKeywordsWithOnlyOneEntry() throws ParseException { - Optional result = parser.parseSingleEntry("@Article{,\n" - + "Keywords={asdf,asdf,asdf},\n" - + "}\n" - + ""); + Optional result = parser.parseSingleEntry(""" + @Article{, + Keywords={asdf,asdf,asdf}, + } + """); BibEntry expectedEntry = new BibEntry(StandardEntryType.Article) .withField(StandardField.KEYWORDS, "asdf,asdf,asdf"); diff --git a/src/test/java/org/jabref/logic/importer/fileformat/MedlinePlainImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/MedlinePlainImporterTest.java index 588185f581f..0ac32eb8cc6 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/MedlinePlainImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/MedlinePlainImporterTest.java @@ -13,6 +13,7 @@ import java.util.stream.Stream; import org.jabref.logic.bibtex.BibEntryAssert; +import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.util.StandardFileType; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; @@ -24,12 +25,15 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class MedlinePlainImporterTest { @@ -48,7 +52,9 @@ private BufferedReader readerForString(String string) { @BeforeEach void setUp() { - importer = new MedlinePlainImporter(); + ImportFormatPreferences importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); + when(importFormatPreferences.bibEntryPreferences().getKeywordSeparator()).thenReturn(','); + importer = new MedlinePlainImporter(importFormatPreferences); } @Test @@ -171,7 +177,10 @@ void multiLineComments() throws IOException { @Test void keyWords() throws IOException { - try (BufferedReader reader = readerForString("PMID-22664795" + "\n" + "MH - Female" + "\n" + "OT - Male")) { + try (BufferedReader reader = readerForString(""" + PMID-22664795 + MH - Female + OT - Male""")) { List actualEntries = importer.importDatabase(reader).getDatabase().getEntries(); BibEntry expectedEntry = new BibEntry();