From efb0c4be5b5d421348d5840352aa0fc0b433d7e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:12:19 +0000 Subject: [PATCH 01/54] Bump commons-cli:commons-cli from 1.6.0 to 1.7.0 (#11236) Bumps commons-cli:commons-cli from 1.6.0 to 1.7.0. --- updated-dependencies: - dependency-name: commons-cli:commons-cli dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a0d3ede962a..371cedbe395 100644 --- a/build.gradle +++ b/build.gradle @@ -170,7 +170,7 @@ dependencies { // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 implementation 'org.bouncycastle:bcprov-jdk18on:1.78' - implementation 'commons-cli:commons-cli:1.6.0' + implementation 'commons-cli:commons-cli:1.7.0' implementation 'org.libreoffice:unoloader:7.6.4' implementation 'org.libreoffice:libreoffice:7.6.4' From 310d002059c0adaf5594efd767c96070fcdf6a14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:12:38 +0000 Subject: [PATCH 02/54] Bump de.undercouch:citeproc-java from 3.0.0 to 3.1.0 (#11235) Bumps [de.undercouch:citeproc-java](https://github.com/michel-kraemer/citeproc-java) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/michel-kraemer/citeproc-java/releases) - [Commits](https://github.com/michel-kraemer/citeproc-java/compare/3.0.0...3.1.0) --- updated-dependencies: - dependency-name: de.undercouch:citeproc-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 371cedbe395..8913346967d 100644 --- a/build.gradle +++ b/build.gradle @@ -240,7 +240,7 @@ dependencies { // route all requests to log4j to SLF4J implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.23.1' - implementation('de.undercouch:citeproc-java:3.0.0') { + implementation('de.undercouch:citeproc-java:3.1.0') { exclude group: 'org.antlr' } From 0914336123147cbc009581aa712e8b4218845964 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:19:38 +0000 Subject: [PATCH 03/54] Bump org.slf4j:slf4j-api from 2.0.12 to 2.0.13 (#11238) Bumps org.slf4j:slf4j-api from 2.0.12 to 2.0.13. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8913346967d..1746b63f006 100644 --- a/build.gradle +++ b/build.gradle @@ -230,7 +230,7 @@ dependencies { implementation 'org.jsoup:jsoup:1.17.2' implementation 'com.konghq:unirest-java:3.14.5' - implementation 'org.slf4j:slf4j-api:2.0.12' + implementation 'org.slf4j:slf4j-api:2.0.13' implementation "org.tinylog:tinylog-api:2.7.0" implementation "org.tinylog:slf4j-tinylog:2.7.0" implementation "org.tinylog:tinylog-impl:2.7.0" From 7fd88d5a089c11a529f77e3c010b85495c841705 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:20:53 +0000 Subject: [PATCH 04/54] Bump org.bouncycastle:bcprov-jdk18on from 1.78 to 1.78.1 (#11237) Bumps [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java) from 1.78 to 1.78.1. - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1746b63f006..76c0d2cefcb 100644 --- a/build.gradle +++ b/build.gradle @@ -168,7 +168,7 @@ dependencies { implementation 'com.h2database:h2-mvstore:2.2.224' // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 - implementation 'org.bouncycastle:bcprov-jdk18on:1.78' + implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1' implementation 'commons-cli:commons-cli:1.7.0' From 0f9e0e74268dd0adb56866c04694dddb8b6f3527 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:34:52 +0000 Subject: [PATCH 05/54] Bump src/main/resources/csl-styles from `d9ca259` to `af6f50b` (#11240) Bumps [src/main/resources/csl-styles](https://github.com/citation-style-language/styles) from `d9ca259` to `af6f50b`. - [Release notes](https://github.com/citation-style-language/styles/releases) - [Commits](https://github.com/citation-style-language/styles/compare/d9ca25977a10d6717d0fc219c1b982863a23a0e7...af6f50b080190ccc7ba0c83906fe36c5ece85ce5) --- updated-dependencies: - dependency-name: src/main/resources/csl-styles dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index d9ca25977a1..af6f50b0801 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit d9ca25977a10d6717d0fc219c1b982863a23a0e7 +Subproject commit af6f50b080190ccc7ba0c83906fe36c5ece85ce5 From dbd9257947147591706c6e0ab6d04aee161bd4e7 Mon Sep 17 00:00:00 2001 From: Jean Perbet Date: Tue, 23 Apr 2024 11:33:27 +0200 Subject: [PATCH 06/54] Keep {} in Markdown rendering (#11196) * fix: removed '{' and '}' swallow when not in command in HTMLChars and adapted tests * doc: updated CHANGELOG.md * fix: added old test cases and adapted them to apply requested changes * fix: fixed small rule in HTMLChars formatter to fix faulty tests * test: added test case in MardownFormatterTest for string with braces * test: added more test cases in MarkdownFormatterTest * fix: fixed checkstyle erros in HTMLChars * test: added test about code between quotes in MardownFormatterTest * feat: added parameter to keep braces to HTMLChars * feat: modified default preview to keep braces when formatting markdown * test: added parameterized test cases to MardownFormatterTest, updated preferences migrations and made keep braces mode name more clear in HTMLChars * fix: fixed issue about preferences migrations * fix: rolling back modif. in HTMLCharsTest and enabled case-insensitive equality for braces keep mode in HTMLChars * test: added test for markdown lists in MarkdowFormatterTest --- CHANGELOG.md | 1 + .../jabref/logic/layout/format/HTMLChars.java | 33 +++++-- .../migrations/PreferencesMigrations.java | 1 + .../jabref/preferences/JabRefPreferences.java | 2 +- .../logic/layout/format/HTMLCharsTest.java | 24 ++++++ .../layout/format/MarkdownFormatterTest.java | 85 +++++++++++++++---- .../migrations/PreferencesMigrationsTest.java | 4 +- 7 files changed, 123 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c2d37b3e28..99fc9dfa8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Fixed - We fixed an issue where entry type with duplicate fields prevented opening existing libraries with custom entry types [#11127](https://github.com/JabRef/jabref/issues/11127) +- We fixed an issue where Markdown rendering removed braces from the text. [#10928](https://github.com/JabRef/jabref/issues/10928) - We fixed an issue when the file was flagged as changed on disk in the case of content selectors or groups. [#9064](https://github.com/JabRef/jabref/issues/9064) - We fixed crash on opening the entry editor when auto completion is enabled. [#11188](https://github.com/JabRef/jabref/issues/11188) - We fixed the usage of the key binding for "Clear search" (default: Escape). [#10764](https://github.com/JabRef/jabref/issues/10764) diff --git a/src/main/java/org/jabref/logic/layout/format/HTMLChars.java b/src/main/java/org/jabref/logic/layout/format/HTMLChars.java index 5bfe60c7bd1..9776f49a504 100644 --- a/src/main/java/org/jabref/logic/layout/format/HTMLChars.java +++ b/src/main/java/org/jabref/logic/layout/format/HTMLChars.java @@ -4,14 +4,14 @@ import java.util.Objects; import java.util.regex.Pattern; -import org.jabref.logic.layout.LayoutFormatter; +import org.jabref.logic.layout.ParamLayoutFormatter; import org.jabref.logic.util.strings.HTMLUnicodeConversionMaps; import org.jabref.model.strings.StringUtil; /** - * This formatter escapes characters so they are suitable for HTML. + * This formatter escapes characters so that they are suitable for HTML. */ -public class HTMLChars implements LayoutFormatter { +public class HTMLChars implements ParamLayoutFormatter { private static final Map HTML_CHARS = HTMLUnicodeConversionMaps.LATEX_HTML_CONVERSION_MAP; /** @@ -23,6 +23,15 @@ public class HTMLChars implements LayoutFormatter { * */ private static final Pattern HTML_ENTITY_PATTERN = Pattern.compile("&(?!(?:[a-z0-9]+|#[0-9]{1,6}|#x[0-9a-fA-F]{1,6});)"); + private boolean keepCurlyBraces = false; + + @Override + public void setArgument(String arg) { + if ("keepCurlyBraces".equalsIgnoreCase(arg)) { + this.keepCurlyBraces = true; + } + } + @Override public String format(String inField) { String field = normalizedField(inField); @@ -49,7 +58,7 @@ public String format(String inField) { escaped = true; incommand = true; currentCommand = new StringBuilder(); - } else if (!incommand && ((c == '{') || (c == '}'))) { + } else if (!this.keepCurlyBraces && !incommand && ((c == '{') || (c == '}'))) { // Swallow the brace. } else if (Character.isLetter(c) || StringUtil.SPECIAL_COMMAND_CHARS.contains(String.valueOf(c))) { escaped = false; @@ -73,7 +82,7 @@ public String format(String inField) { String commandBody; if (c == '{') { String part = StringUtil.getPart(field, i, false); - i += part.length(); + i += this.keepCurlyBraces ? part.length() + 1 : part.length(); commandBody = part; } else { commandBody = field.substring(i, i + 1); @@ -83,7 +92,6 @@ public String format(String inField) { sb.append(Objects.requireNonNullElse(result, commandBody)); incommand = false; - escaped = false; } else { // Are we already at the end of the string? if ((i + 1) == field.length()) { @@ -109,11 +117,14 @@ public String format(String inField) { String tag = getHTMLTag(command); if (!tag.isEmpty()) { String part = StringUtil.getPart(field, i, true); + if (this.keepCurlyBraces && (c == '{' || (c == '}'))) { + i++; + } i += part.length(); sb.append('<').append(tag).append('>').append(part).append("'); } else if (c == '{') { String argument = StringUtil.getPart(field, i, true); - i += argument.length(); + i += this.keepCurlyBraces ? argument.length() + 1 : argument.length(); // handle common case of general latex command String result = HTML_CHARS.get(command + argument); // If found, then use translated version. If not, then keep @@ -133,10 +144,14 @@ public String format(String inField) { } else if (c == '}') { // This end brace terminates a command. This can be the case in // constructs like {\aa}. The correct behaviour should be to - // substitute the evaluated command and swallow the brace: + // substitute the evaluated command. String result = HTML_CHARS.get(command); // If the command is unknown, just print it: sb.append(Objects.requireNonNullElse(result, command)); + // We only keep the brace if we are in 'KEEP' mode. + if (this.keepCurlyBraces) { + sb.append(c); + } } else { String result = HTML_CHARS.get(command); sb.append(Objects.requireNonNullElse(result, command)); @@ -170,7 +185,7 @@ private String normalizedField(String inField) { .replaceAll("[\\n]{2,}", "

") // Replace double line breaks with

.replace("\n", "
") // Replace single line breaks with
.replace("\\$", "$") // Replace \$ with $ - .replaceAll("\\$([^$]*)\\$", "\\{$1\\}"); + .replaceAll("\\$([^$]*)\\$", this.keepCurlyBraces ? "\\\\{$1\\\\}" : "$1}"); } private String getHTMLTag(String latexCommand) { diff --git a/src/main/java/org/jabref/migrations/PreferencesMigrations.java b/src/main/java/org/jabref/migrations/PreferencesMigrations.java index 92d60f49632..5a364ad06c5 100644 --- a/src/main/java/org/jabref/migrations/PreferencesMigrations.java +++ b/src/main/java/org/jabref/migrations/PreferencesMigrations.java @@ -331,6 +331,7 @@ protected static void upgradePreviewStyle(JabRefPreferences prefs) { String currentPreviewStyle = prefs.get(JabRefPreferences.PREVIEW_STYLE); String migratedStyle = currentPreviewStyle.replace("\\begin{review}

Review: \\format[HTMLChars]{\\review} \\end{review}", "\\begin{comment}

Comment: \\format[Markdown,HTMLChars]{\\comment} \\end{comment}") .replace("\\format[HTMLChars]{\\comment}", "\\format[Markdown,HTMLChars]{\\comment}") + .replace("\\format[Markdown,HTMLChars]{\\comment}", "\\format[Markdown,HTMLChars(keepCurlyBraces)]{\\comment}") .replace("\\bibtextype\\begin{bibtexkey} (\\bibtexkey)", "\\bibtextype\\begin{citationkey} (\\citationkey)") .replace("\\end{bibtexkey}
__NEWLINE__", "\\end{citationkey}

__NEWLINE__"); prefs.put(JabRefPreferences.PREVIEW_STYLE, migratedStyle); diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index 55de7b43b54..d9c747b3a1e 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -821,7 +821,7 @@ private JabRefPreferences() { "\\begin{pages}
p. \\format[FormatPagesForHTML]{\\pages}\\end{pages}__NEWLINE__" + "\\begin{abstract}

Abstract: \\format[HTMLChars]{\\abstract} \\end{abstract}__NEWLINE__" + "\\begin{owncitation}

Own citation: \\format[HTMLChars]{\\owncitation} \\end{owncitation}__NEWLINE__" + - "\\begin{comment}

Comment: \\format[Markdown,HTMLChars]{\\comment}\\end{comment}__NEWLINE__" + + "\\begin{comment}

Comment: \\format[Markdown,HTMLChars(keepCurlyBraces)]{\\comment}\\end{comment}__NEWLINE__" + "__NEWLINE__"); // set default theme diff --git a/src/test/java/org/jabref/logic/layout/format/HTMLCharsTest.java b/src/test/java/org/jabref/logic/layout/format/HTMLCharsTest.java index a31c04c7205..045ab4b5adf 100644 --- a/src/test/java/org/jabref/logic/layout/format/HTMLCharsTest.java +++ b/src/test/java/org/jabref/logic/layout/format/HTMLCharsTest.java @@ -3,6 +3,7 @@ import java.util.stream.Stream; import org.jabref.logic.layout.LayoutFormatter; +import org.jabref.logic.layout.ParamLayoutFormatter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -15,10 +16,13 @@ public class HTMLCharsTest { private LayoutFormatter layout; + private ParamLayoutFormatter layoutKeep; @BeforeEach public void setUp() { layout = new HTMLChars(); + layoutKeep = new HTMLChars(); + layoutKeep.setArgument("keepCurlyBraces"); } private static Stream provideBasicFormattingData() { @@ -37,6 +41,7 @@ private static Stream provideBasicFormattingData() { Arguments.of("ä", "{\\\"a}"), Arguments.of("ä", "\\\"a"), Arguments.of("Ç", "{\\c{C}}"), + Arguments.of("Ç", "\\c{C}"), Arguments.of("&Oogon;ı", "\\k{O}\\i"), Arguments.of("ñ ñ í ı ı", "\\~{n} \\~n \\'i \\i \\i") ); @@ -144,4 +149,23 @@ private static Stream provideHTMLEntityFormattingData() { void hTMLEntityFormatting(String expected, String input) { assertEquals(expected, layout.format(input)); } + + private static Stream provideBracesKeepFormattingData() { + return Stream.of( + Arguments.of("{ä}", "{\\\"{a}}"), + Arguments.of("{ä}", "{\\\"a}"), + Arguments.of("{Ç}", "{\\c{C}}"), + Arguments.of("{hallo}", "{\\emph hallo}"), + Arguments.of("{hallo}", "{\\textbf hallo}"), + Arguments.of("{hallo}", "{\\bf hallo}"), + Arguments.of("{'}", "{\\textquotesingle}"), + Arguments.of("{{ test }}", "{{ test }}") + ); + } + + @ParameterizedTest + @MethodSource("provideBracesKeepFormattingData") + void bracesKeepFormatting(String expected, String input) { + assertEquals(expected, layoutKeep.format(input)); + } } diff --git a/src/test/java/org/jabref/logic/layout/format/MarkdownFormatterTest.java b/src/test/java/org/jabref/logic/layout/format/MarkdownFormatterTest.java index e5e1f9281e6..2bfb5ef4caa 100644 --- a/src/test/java/org/jabref/logic/layout/format/MarkdownFormatterTest.java +++ b/src/test/java/org/jabref/logic/layout/format/MarkdownFormatterTest.java @@ -1,10 +1,14 @@ package org.jabref.logic.layout.format; +import java.util.stream.Stream; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; class MarkdownFormatterTest { @@ -17,23 +21,74 @@ void setUp() { } @Test - void formatWhenFormattingPlainTextThenReturnsTextWrappedInParagraph() { - assertEquals("

Hello World

", markdownFormatter.format("Hello World")); - } - - @Test - void formatWhenFormattingComplexMarkupThenReturnsOnlyOneLine() { - assertFalse(markdownFormatter.format("Markup\n\n* list item one\n* list item 2\n\n rest").contains("\n")); + void formatWhenFormattingNullThenThrowsException() { + Exception exception = assertThrows(NullPointerException.class, () -> markdownFormatter.format(null)); + assertEquals("Field Text should not be null, when handed to formatter", exception.getMessage()); } - @Test - void formatWhenFormattingEmptyStringThenReturnsEmptyString() { - assertEquals("", markdownFormatter.format("")); + private static Stream provideMarkdownAndHtml() { + return Stream.of( + Arguments.of("Hello World", "

Hello World

"), + Arguments.of(""" + Markup + + * list item one + * list item two + + rest + """, + "

Markup

  • list item one
  • list item two

rest

" + ), + Arguments.of(""" + ``` + Hello World + ``` + """, + "
Hello World 
" + ), + Arguments.of(""" + First line + + Second line + + ```java + String test; + ``` + """, + "

First line

Second line

String test; 
" + ), + Arguments.of(""" + Some text. + ```javascript + let test = "Hello World"; + ``` + + ```java + String test = "Hello World"; + ``` + Some more text. + """, + "

Some text.

let test = "Hello World"; " +
+                            "
String test = "Hello World"; " +
+                            "

Some more text.

" + ), + Arguments.of(""" + Some text. + + ```java + int foo = 0; + foo = 1; + + ``` + """, + "

Some text.

int foo = 0; foo = 1;  
" + ) + ); } - @Test - void formatWhenFormattingNullThenThrowsException() { - Exception exception = assertThrows(NullPointerException.class, () -> markdownFormatter.format(null)); - assertEquals("Field Text should not be null, when handed to formatter", exception.getMessage()); + @ParameterizedTest + @MethodSource("provideMarkdownAndHtml") + void formatWhenFormattingCodeBlockThenReturnsCodeBlockInHtml(String markdown, String expectedHtml) { + assertEquals(expectedHtml, markdownFormatter.format(markdown)); } } diff --git a/src/test/java/org/jabref/migrations/PreferencesMigrationsTest.java b/src/test/java/org/jabref/migrations/PreferencesMigrationsTest.java index 0a89c471788..9b3f8f0c145 100644 --- a/src/test/java/org/jabref/migrations/PreferencesMigrationsTest.java +++ b/src/test/java/org/jabref/migrations/PreferencesMigrationsTest.java @@ -84,8 +84,8 @@ void previewStyleReviewToComment() { String newPreviewStyle = "__NEWLINE__" + "Customized preview style using reviews and comments:__NEWLINE__" - + "\\begin{comment}

Comment: \\format[Markdown,HTMLChars]{\\comment} \\end{comment}__NEWLINE__" - + "\\begin{comment} Something: \\format[Markdown,HTMLChars]{\\comment} special \\end{comment}__NEWLINE__" + + "\\begin{comment}

Comment: \\format[Markdown,HTMLChars(keepCurlyBraces)]{\\comment} \\end{comment}__NEWLINE__" + + "\\begin{comment} Something: \\format[Markdown,HTMLChars(keepCurlyBraces)]{\\comment} special \\end{comment}__NEWLINE__" + "
__NEWLINE__"; when(prefs.get(JabRefPreferences.PREVIEW_STYLE)).thenReturn(oldPreviewStyle); From 073fbafa9b869949a7180430029b0e9854fdc204 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 23 Apr 2024 12:27:18 +0200 Subject: [PATCH 07/54] Update external-libraries.md (#11242) --- external-libraries.md | 50 +++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/external-libraries.md b/external-libraries.md index 16b5165fe82..96695fdc1b7 100644 --- a/external-libraries.md +++ b/external-libraries.md @@ -691,9 +691,13 @@ License: BSD-3-Clause ## Sorted list of runtime dependencies output by gradle -1. `./gradlew dependencies > build/dependencies.txt` -2. Manually edit depedencies.txt to contain the tree of "compileClasspath" and "implementation" only. Otherwise, libraries such as "Apache Commons Lang 3" are missed. Ensure LF as line ending. -3. (on WSL) `sed 's/[^a-z]*//' < build/dependencies.txt | sed "s/\(.*\) .*/\1/" | grep -v "\->" | sort | uniq > build/dependencies-for-external-libraries.txt` +1. `./gradlew dependencyReport --configuration compileClasspath` +2. Fix `build/reports/project/dependencies.txt` + + - Change line endings to `LF` + - Remove text above and below the tree + +3. (on WSL) `sed 's/[^a-z]*//' < build/reports/project/dependencies.txt | sed "s/\(.*\) .*/\1/" | grep -v "\->" | sort | uniq > build/dependencies-for-external-libraries.txt` ```text at.favre.lib:hkdf:1.1.0 @@ -809,7 +813,7 @@ org.apache.pdfbox:fontbox:3.0.2 org.apache.pdfbox:pdfbox-io:3.0.2 org.apache.pdfbox:pdfbox:3.0.2 org.apache.pdfbox:xmpbox:3.0.2 -org.bouncycastle:bcprov-jdk18on:1.77 +org.bouncycastle:bcprov-jdk18on:1.78 org.checkerframework:checker-qual:3.42.0 org.codehaus.woodstox:stax2-api:4.2 org.controlsfx:controlsfx:11.2.1 @@ -818,24 +822,24 @@ org.fxmisc.flowless:flowless:0.7.2 org.fxmisc.richtext:richtextfx:0.11.2 org.fxmisc.undo:undofx:2.1.1 org.fxmisc.wellbehaved:wellbehavedfx:0.3.3 -org.glassfish.grizzly:grizzly-framework:4.0.1 -org.glassfish.grizzly:grizzly-http-server:4.0.1 -org.glassfish.grizzly:grizzly-http:4.0.1 +org.glassfish.grizzly:grizzly-framework:4.0.2 +org.glassfish.grizzly:grizzly-http-server:4.0.2 +org.glassfish.grizzly:grizzly-http:4.0.2 org.glassfish.hk2.external:aopalliance-repackaged:3.1.0 org.glassfish.hk2:hk2-api:3.1.0 -org.glassfish.hk2:hk2-locator:3.0.5 +org.glassfish.hk2:hk2-locator:3.0.6 org.glassfish.hk2:hk2-utils:3.1.0 org.glassfish.hk2:osgi-resource-locator:1.0.3 org.glassfish.jaxb:jaxb-core:4.0.3 org.glassfish.jaxb:jaxb-runtime:4.0.3 org.glassfish.jaxb:txw2:4.0.3 -org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.5 -org.glassfish.jersey.core:jersey-client:3.1.5 -org.glassfish.jersey.core:jersey-common:3.1.5 -org.glassfish.jersey.core:jersey-server:3.1.5 -org.glassfish.jersey.inject:jersey-hk2:3.1.5 +org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.6 +org.glassfish.jersey.core:jersey-client:3.1.6 +org.glassfish.jersey.core:jersey-common:3.1.6 +org.glassfish.jersey.core:jersey-server:3.1.6 +org.glassfish.jersey.inject:jersey-hk2:3.1.6 org.jabref:afterburner.fx:2.0.0 -org.javassist:javassist:3.29.2-GA +org.javassist:javassist:3.30.2-GA org.jbibtex:jbibtex:1.0.20 org.jetbrains:annotations:24.0.1 org.jooq:jool:0.9.15 @@ -850,18 +854,18 @@ org.kordamp.ikonli:ikonli-materialdesign2-pack:12.3.1 org.libreoffice:libreoffice:7.6.4 org.libreoffice:unoloader:7.6.4 org.mariadb.jdbc:mariadb-java-client:2.7.9 -org.openjfx:javafx-base:22 -org.openjfx:javafx-controls:22 -org.openjfx:javafx-fxml:22 -org.openjfx:javafx-graphics:22 -org.openjfx:javafx-media:22 -org.openjfx:javafx-swing:22 -org.openjfx:javafx-web:22 +org.openjfx:javafx-base:22.0.1 +org.openjfx:javafx-controls:22.0.1 +org.openjfx:javafx-fxml:22.0.1 +org.openjfx:javafx-graphics:22.0.1 +org.openjfx:javafx-media:22.0.1 +org.openjfx:javafx-swing:22.0.1 +org.openjfx:javafx-web:22.0.1 org.postgresql:postgresql:42.7.3 org.reactfx:reactfx:2.0-M5 org.scala-lang:scala-library:2.13.8 -org.slf4j:jul-to-slf4j:2.0.12 -org.slf4j:slf4j-api:2.0.12 +org.slf4j:jul-to-slf4j:2.0.13 +org.slf4j:slf4j-api:2.0.13 org.tinylog:slf4j-tinylog:2.7.0 org.tinylog:tinylog-api:2.7.0 org.tinylog:tinylog-impl:2.7.0 From 47dbcdb8caf97a5c455eb98337d74add38c74c5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:52:47 +0000 Subject: [PATCH 08/54] Bump com.dlsc.gemsfx:gemsfx from 2.8.0 to 2.9.0 (#11239) * Bump com.dlsc.gemsfx:gemsfx from 2.8.0 to 2.9.0 Bumps [com.dlsc.gemsfx:gemsfx](https://github.com/dlsc-software-consulting-gmbh/GemsFX) from 2.8.0 to 2.9.0. - [Release notes](https://github.com/dlsc-software-consulting-gmbh/GemsFX/releases) - [Changelog](https://github.com/dlsc-software-consulting-gmbh/GemsFX/blob/master/CHANGELOG.md) - [Commits](https://github.com/dlsc-software-consulting-gmbh/GemsFX/compare/v2.8.0...v2.9.0) --- updated-dependencies: - dependency-name: com.dlsc.gemsfx:gemsfx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Update transitive dependency --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Oliver Kopp --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 76c0d2cefcb..0ebf42a254e 100644 --- a/build.gradle +++ b/build.gradle @@ -218,12 +218,15 @@ dependencies { } implementation 'org.fxmisc.flowless:flowless:0.7.2' implementation 'org.fxmisc.richtext:richtextfx:0.11.2' - implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.8.0') { + implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.9.0') { exclude module: 'javax.inject' // Split package, use only jakarta.inject exclude module: 'commons-lang3' exclude group: 'org.openjfx' exclude group: 'org.apache.logging.log4j' + exclude group: 'tech.units' } + // Required by gemsfx + implementation 'tech.units:indriya:2.2' implementation 'org.controlsfx:controlsfx:11.2.1' From 91f956cca061ad33141a22c8ff3b08c7e145ed72 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 29 Apr 2024 08:46:27 +0200 Subject: [PATCH 09/54] Add check for PRs from "main" (#11252) * Add check for PRs from "main" * Fix workflow * Try multiline message * More emoji * Reword --- .github/workflows/tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fa4bc3f0276..34e34a5086c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -335,3 +335,12 @@ jobs: show-progress: 'false' - name: Merge Conflict finder uses: olivernybroe/action-conflict-finder@v4.0 + other_than_main: + name: Source branch is other than "main" + runs-on: ubuntu-latest + steps: + - if: github.head_ref == 'main' + uses: actions/github-script@v6 + with: + script: | + core.setFailed('Pull requests should come from a branch other than "main"\n\nšŸ‘‰ Please read https://devdocs.jabref.org/contributing again carefully. šŸ‘ˆ') From 70b7748fdd0696357d9bfd9f0fa5b994cb1e0c07 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb <52158423+LoayGhreeb@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:07:02 +0300 Subject: [PATCH 10/54] Add directory monitor for Latex citations (#11245) * Add directory monitor * Ignore parsing nested files * Add listener for LaTex directory, update LatexCitationsTab * Update CHANGELOG.md * Decision record * Refine ADR-030 * Update module-info.java * handle non-existing directories * Java Doc * Java Doc * Move directoryMonitor to Globals --------- Co-authored-by: Oliver Kopp --- CHANGELOG.md | 1 + build.gradle | 2 + .../0028-http-return-bibtex-string.md | 2 - .../0029-cff-export-multiple-entries.md | 5 +- ...che-commons-io-for-directory-monitoring.md | 48 ++++ external-libraries.md | 23 +- src/main/java/module-info.java | 11 +- src/main/java/org/jabref/gui/Globals.java | 15 ++ src/main/java/org/jabref/gui/LibraryTab.java | 16 +- .../java/org/jabref/gui/StateManager.java | 2 +- .../jabref/gui/entryeditor/EntryEditor.java | 5 +- .../gui/entryeditor/LatexCitationsTab.java | 43 ++-- .../LatexCitationsTabViewModel.java | 213 ++++++++++++------ .../gui/util/DefaultDirectoryMonitor.java | 54 +++++ .../logic/texparser/CitationFinder.java | 56 ----- .../logic/texparser/DefaultLatexParser.java | 25 +- .../model/texparser/LatexParserResults.java | 23 +- .../jabref/model/util/DirectoryMonitor.java | 25 ++ .../model/util/DirectoryMonitorManager.java | 36 +++ src/main/resources/l10n/JabRef_en.properties | 2 +- 20 files changed, 412 insertions(+), 195 deletions(-) create mode 100644 docs/decisions/0030-use-apache-commons-io-for-directory-monitoring.md create mode 100644 src/main/java/org/jabref/gui/util/DefaultDirectoryMonitor.java delete mode 100644 src/main/java/org/jabref/logic/texparser/CitationFinder.java create mode 100644 src/main/java/org/jabref/model/util/DirectoryMonitor.java create mode 100644 src/main/java/org/jabref/model/util/DirectoryMonitorManager.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 99fc9dfa8fa..134ec7736be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added tooltip on main table cells that shows cell content or cell content and entry preview if set in preferences. [10925](https://github.com/JabRef/jabref/issues/10925) - We added the ability to add a keyword/crossref when typing the separator character (e.g., comma) in the keywords/crossref fields. [#11178](https://github.com/JabRef/jabref/issues/11178) - We added an exporter and improved the importer for Endnote XML format. [#11137](https://github.com/JabRef/jabref/issues/11137) +- We added support for automatically update LaTeX citations when a LaTeX file is created, removed, or modified. [#10585](https://github.com/JabRef/jabref/issues/10585) ### Changed diff --git a/build.gradle b/build.gradle index 0ebf42a254e..28db2f20df6 100644 --- a/build.gradle +++ b/build.gradle @@ -296,6 +296,8 @@ dependencies { // YAML formatting implementation 'org.yaml:snakeyaml:2.2' + implementation 'commons-io:commons-io:2.16.1' + testImplementation 'io.github.classgraph:classgraph:4.8.170' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.2' diff --git a/docs/decisions/0028-http-return-bibtex-string.md b/docs/decisions/0028-http-return-bibtex-string.md index e60a931d54a..bc8ff9394eb 100644 --- a/docs/decisions/0028-http-return-bibtex-string.md +++ b/docs/decisions/0028-http-return-bibtex-string.md @@ -2,8 +2,6 @@ nav_order: 28 parent: Decision Records --- - - # Return BibTeX string and CSL Item JSON in the API ## Context and Problem Statement diff --git a/docs/decisions/0029-cff-export-multiple-entries.md b/docs/decisions/0029-cff-export-multiple-entries.md index 179ceab745d..ec1dab8cfc0 100644 --- a/docs/decisions/0029-cff-export-multiple-entries.md +++ b/docs/decisions/0029-cff-export-multiple-entries.md @@ -1,10 +1,7 @@ --- -nav_order: 28 +nav_order: 29 parent: Decision Records --- - - - # Exporting multiple entries to CFF ## Context and Problem Statement diff --git a/docs/decisions/0030-use-apache-commons-io-for-directory-monitoring.md b/docs/decisions/0030-use-apache-commons-io-for-directory-monitoring.md new file mode 100644 index 00000000000..42a762132c8 --- /dev/null +++ b/docs/decisions/0030-use-apache-commons-io-for-directory-monitoring.md @@ -0,0 +1,48 @@ +--- +nav_order: 30 +parent: Decision Records +--- +# Use Apache Commons IO for directory monitoring + +## Context and Problem Statement + +In JabRef, there is a need to add a directory monitor that will listen for changes in a specified directory. + +Currently, the monitor is used to automatically update the [LaTeX Citations](https://docs.jabref.org/advanced/entryeditor/latex-citations) when a LaTeX file in the LaTeX directory is created, removed, or modified ([#10585](https://github.com/JabRef/jabref/issues/10585)). +Additionally, this monitor will be used to create a dynamic group that mirrors the file system structure ([#10930](https://github.com/JabRef/jabref/issues/10930)). + +## Considered Options + +* Use [java.nio.file.WatchService](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/nio/file/WatchService.html) +* Use [io.methvin.watcher.DirectoryWatcher](https://github.com/gmethvin/directory-watcher) +* Use [org.apache.commons.io.monitor](https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/monitor/package-summary.html) + +## Decision Outcome + +Chosen option: "Use [org.apache.commons.io.monitor](https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/monitor/package-summary.html)", because comes out best (see below). + +## Pros and Cons of the Options + +### java.nio.file.WatchService + +* Good, because it is a standard Java API for watching directories. +* Good, because it does not need polling, it is event-based for most operating systems. +* Bad, because: + 1. Does not detect files coming together with a new folder (JDK issue: [JDK-8162948](https://bugs.openjdk.org/browse/JDK-8162948)). + 2. Deleting a subdirectory does not detect deleted files in that directory. + 3. Access denied when trying to delete the recursively watched directory on Windows (JDK issue: [JDK-6972833](https://bugs.openjdk.org/browse/JDK-6972833)). + 4. Implemented on macOS by the generic `PollingWatchService`. (JDK issue: [JDK-8293067](https://bugs.openjdk.org/browse/JDK-8293067)) + +### io.methvin.watcher.DirectoryWatcher + +* Good, because it implemented on top of the `java.nio.file.WatchService`, which is a standard Java API for watching directories. +* Good, because it resolves some of the issues of the `java.nio.file.WatchService`. + * Uses`ExtendedWatchEventModifier.FILE_TREE` on Windows, which resolves issues (1, 3) of the `java.nio.file.WatchService`. + * On macOS have native implementation based on the Carbon File System Events API, this resolves issue (4) of the `java.nio.file.WatchService`. +* Bad, because issue (2) of the `java.nio.file.WatchService` is not resolved. + +### org.apache.commons.io.monitor + +* Good, because there are no observed issues. +* Good, because can handle huge amount of files without overflowing. +* Bad, because it uses a polling mechanism at fixed intervals, which can waste CPU cycles if no change occurs. diff --git a/external-libraries.md b/external-libraries.md index 96695fdc1b7..a4e483bb84d 100644 --- a/external-libraries.md +++ b/external-libraries.md @@ -294,6 +294,12 @@ Project: Apache Commons Digester URL: https://commons.apache.org/proper/commons-digester/ ``` +```yaml +Id: commons-io:commons-io +Project: Apache Commons IO +URL: https://commons.apache.org/proper/commons-io/ +``` + ```yaml Id: de.rototor.jeuclid:jeuclid-core Project: JEuclid @@ -537,7 +543,7 @@ Id: com.ibm.icu:* Project: International Components for Unicode URL: https://icu.unicode.org/ License: Unicode License (https://www.unicode.org/copyright.html) -Note: Our own fork https://github.com/JabRef/icu. Upstream PR: https://github.com/unicode-org/icu/pull/2127 +Note: Our own fork https://github.com/JabRef/icu. [Upstream PR](https://github.com/unicode-org/icu/pull/2127) Path: lib/icu4j.jar SourcePath: lib/ic4j-src.jar ``` @@ -579,49 +585,49 @@ License: LGPL-2.1-or-later ```yaml Id: org.openjfx:javafx-base -Project JavaFX +Project: JavaFX URL: https://openjfx.io/ License: GPL-2.0 WITH Classpath-exception-2.0 ``` ```yaml Id: org.openjfx:javafx-controls -Project JavaFX +Project: JavaFX URL: https://openjfx.io/ License: GPL-2.0 WITH Classpath-exception-2.0 ``` ```yaml Id: org.openjfx:javafx-fxml -Project JavaFX +Project: JavaFX URL: https://openjfx.io/ License: GPL-2.0 WITH Classpath-exception-2.0 ``` ```yaml Id: org.openjfx:javafx-graphics -Project JavaFX +Project: JavaFX URL: https://openjfx.io/ License: GPL-2.0 WITH Classpath-exception-2.0 ``` ```yaml Id: org.openjfx:javafx-media -Project JavaFX +Project: JavaFX URL: https://openjfx.io/ License: GPL-2.0 WITH Classpath-exception-2.0 ``` ```yaml Id: org.openjfx:javafx-swing -Project JavaFX +Project: JavaFX URL: https://openjfx.io/ License: GPL-2.0 WITH Classpath-exception-2.0 ``` ```yaml Id: org.openjfx:javafx-web -Project JavaFX +Project: JavaFX URL: https://openjfx.io/ License: GPL-2.0 WITH Classpath-exception-2.0 ``` @@ -765,6 +771,7 @@ commons-cli:commons-cli:1.6.0 commons-codec:commons-codec:1.16.0 commons-collections:commons-collections:3.2.2 commons-digester:commons-digester:2.1 +commons-io:commons-io:2.16.1 commons-logging:commons-logging:1.2 commons-validator:commons-validator:1.7 de.rototor.jeuclid:jeuclid-core:3.1.11 diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 597270bf308..352ef69c95b 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -54,6 +54,9 @@ requires java.prefs; requires com.fasterxml.aalto; + // YAML + requires org.yaml.snakeyaml; + // Annotations (@PostConstruct) requires jakarta.annotation; requires jakarta.inject; @@ -88,13 +91,14 @@ uses org.mariadb.jdbc.credential.CredentialPlugin; // Apache Commons and other (similar) helper libraries + requires com.google.common; + requires io.github.javadiffutils; + requires java.string.similarity; requires org.apache.commons.cli; requires org.apache.commons.csv; + requires org.apache.commons.io; requires org.apache.commons.lang3; requires org.apache.commons.text; - requires com.google.common; - requires io.github.javadiffutils; - requires java.string.similarity; requires com.github.tomtung.latex2unicode; requires fastparse; @@ -146,5 +150,4 @@ requires de.saxsys.mvvmfx.validation; requires dd.plist; requires mslinks; - requires org.yaml.snakeyaml; } diff --git a/src/main/java/org/jabref/gui/Globals.java b/src/main/java/org/jabref/gui/Globals.java index 4bbb5dbb2d8..bb8a45f8d8b 100644 --- a/src/main/java/org/jabref/gui/Globals.java +++ b/src/main/java/org/jabref/gui/Globals.java @@ -5,6 +5,7 @@ import org.jabref.gui.remote.CLIMessageHandler; import org.jabref.gui.telemetry.Telemetry; import org.jabref.gui.undo.CountingUndoManager; +import org.jabref.gui.util.DefaultDirectoryMonitor; import org.jabref.gui.util.DefaultFileUpdateMonitor; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.TaskExecutor; @@ -14,6 +15,7 @@ import org.jabref.logic.remote.server.RemoteListenerServerManager; import org.jabref.logic.util.BuildInfo; import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.util.DirectoryMonitor; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; @@ -65,6 +67,7 @@ public class Globals { private static KeyBindingRepository keyBindingRepository; private static DefaultFileUpdateMonitor fileUpdateMonitor; + private static DefaultDirectoryMonitor directoryMonitor; private Globals() { } @@ -92,6 +95,13 @@ public static synchronized FileUpdateMonitor getFileUpdateMonitor() { return fileUpdateMonitor; } + public static DirectoryMonitor getDirectoryMonitor() { + if (directoryMonitor == null) { + directoryMonitor = new DefaultDirectoryMonitor(); + } + return directoryMonitor; + } + // Background tasks public static void startBackgroundTasks() { // TODO Currently deactivated due to incompatibilities in XML @@ -109,6 +119,11 @@ public static void shutdownThreadPools() { if (fileUpdateMonitor != null) { fileUpdateMonitor.shutdown(); } + + if (directoryMonitor != null) { + directoryMonitor.shutdown(); + } + JabRefExecutorService.INSTANCE.shutdownEverything(); } diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 48231b2f967..02ee691c747 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -84,6 +84,7 @@ import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.FieldFactory; import org.jabref.model.entry.field.StandardField; +import org.jabref.model.util.DirectoryMonitorManager; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; @@ -127,9 +128,9 @@ private enum PanelMode { MAIN_TABLE, MAIN_TABLE_AND_ENTRY_EDITOR } // Indicates whether the tab is loading data using a dataloading task // The constructors take care to the right true/false assignment during start. - private SimpleBooleanProperty loading = new SimpleBooleanProperty(false); + private final SimpleBooleanProperty loading = new SimpleBooleanProperty(false); - // initally, the dialog is loading, not saving + // initially, the dialog is loading, not saving private boolean saving = false; private PersonNameSuggestionProvider searchAutoCompleter; @@ -151,6 +152,7 @@ private enum PanelMode { MAIN_TABLE, MAIN_TABLE_AND_ENTRY_EDITOR } private final IndexingTaskManager indexingTaskManager; private final TaskExecutor taskExecutor; + private final DirectoryMonitorManager directoryMonitorManager; private LibraryTab(BibDatabaseContext bibDatabaseContext, LibraryTabContainer tabContainer, @@ -171,6 +173,7 @@ private LibraryTab(BibDatabaseContext bibDatabaseContext, this.entryTypesManager = entryTypesManager; this.indexingTaskManager = new IndexingTaskManager(taskExecutor); this.taskExecutor = taskExecutor; + this.directoryMonitorManager = new DirectoryMonitorManager(Globals.getDirectoryMonitor()); bibDatabaseContext.getDatabase().registerListener(this); bibDatabaseContext.getMetaData().registerListener(this); @@ -832,6 +835,11 @@ private void onClosed(Event event) { } catch (RuntimeException e) { LOGGER.error("Problem when closing change monitor", e); } + try { + directoryMonitorManager.unregister(); + } catch (RuntimeException e) { + LOGGER.error("Problem when closing directory monitor", e); + } try { PdfIndexerManager.shutdownIndexer(bibDatabaseContext); } catch (RuntimeException e) { @@ -864,6 +872,10 @@ public BibDatabaseContext getBibDatabaseContext() { return this.bibDatabaseContext; } + public DirectoryMonitorManager getDirectoryMonitorManager() { + return directoryMonitorManager; + } + public boolean isSaving() { return saving; } diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 2c842cd213d..010375fc1c5 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -190,7 +190,7 @@ public ObservableList> getBackgroundTasks() { } public void addBackgroundTask(BackgroundTask backgroundTask, Task task) { - this.backgroundTasks.add(0, new Pair<>(backgroundTask, task)); + this.backgroundTasks.addFirst(new Pair<>(backgroundTask, task)); } public EasyBinding getAnyTaskRunning() { diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index e2621d4cec0..220d11e2299 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -55,6 +55,7 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.entry.field.Field; +import org.jabref.model.util.DirectoryMonitorManager; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; @@ -82,6 +83,7 @@ public class EntryEditor extends BorderPane { private final BibDatabaseContext databaseContext; private final EntryEditorPreferences entryEditorPreferences; private final ExternalFilesEntryLinker fileLinker; + private final DirectoryMonitorManager directoryMonitorManager; private Subscription typeSubscription; @@ -121,6 +123,7 @@ public class EntryEditor extends BorderPane { public EntryEditor(LibraryTab libraryTab) { this.libraryTab = libraryTab; this.databaseContext = libraryTab.getBibDatabaseContext(); + this.directoryMonitorManager = libraryTab.getDirectoryMonitorManager(); ViewLoader.view(this) .root(this) @@ -307,7 +310,7 @@ private List createTabs() { bibEntryTypesManager, keyBindingRepository); entryEditorTabs.add(sourceTab); - entryEditorTabs.add(new LatexCitationsTab(databaseContext, preferencesService, taskExecutor, dialogService)); + entryEditorTabs.add(new LatexCitationsTab(databaseContext, preferencesService, dialogService, directoryMonitorManager)); entryEditorTabs.add(new FulltextSearchResultsTab(stateManager, preferencesService, dialogService, taskExecutor)); return entryEditorTabs; diff --git a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTab.java b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTab.java index 2272316bb8f..0be86194c87 100644 --- a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTab.java @@ -17,10 +17,10 @@ import org.jabref.gui.DialogService; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.texparser.CitationsDisplay; -import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; +import org.jabref.model.util.DirectoryMonitorManager; import org.jabref.preferences.PreferencesService; import com.tobiasdiez.easybind.EasyBind; @@ -33,9 +33,17 @@ public class LatexCitationsTab extends EntryEditorTab { private final ProgressIndicator progressIndicator; private final CitationsDisplay citationsDisplay; - public LatexCitationsTab(BibDatabaseContext databaseContext, PreferencesService preferencesService, - TaskExecutor taskExecutor, DialogService dialogService) { - this.viewModel = new LatexCitationsTabViewModel(databaseContext, preferencesService, taskExecutor, dialogService); + public LatexCitationsTab(BibDatabaseContext databaseContext, + PreferencesService preferencesService, + DialogService dialogService, + DirectoryMonitorManager directoryMonitorManager) { + + this.viewModel = new LatexCitationsTabViewModel( + databaseContext, + preferencesService, + dialogService, + directoryMonitorManager); + this.searchPane = new GridPane(); this.progressIndicator = new ProgressIndicator(); this.citationsDisplay = new CitationsDisplay(); @@ -66,6 +74,11 @@ private void setSearchPane() { searchPane.setId("citationsPane"); setContent(searchPane); + HBox latexDirectoryBox = getLatexDirectoryBox(); + VBox citationsPane = getCitationsPane(); + VBox notFoundPane = getNotFoundPane(); + VBox errorPane = getErrorPane(); + EasyBind.subscribe(viewModel.statusProperty(), status -> { searchPane.getChildren().clear(); switch (status) { @@ -73,36 +86,35 @@ private void setSearchPane() { searchPane.add(progressIndicator, 0, 0); break; case CITATIONS_FOUND: - searchPane.add(getCitationsPane(), 0, 0); + searchPane.add(citationsPane, 0, 0); break; case NO_RESULTS: - searchPane.add(getNotFoundPane(), 0, 0); + searchPane.add(notFoundPane, 0, 0); break; case ERROR: - searchPane.add(getErrorPane(), 0, 0); + searchPane.add(errorPane, 0, 0); break; } - searchPane.add(getLatexDirectoryBox(), 0, 1); + searchPane.add(latexDirectoryBox, 0, 1); }); } private HBox getLatexDirectoryBox() { Text latexDirectoryText = new Text(Localization.lang("Current search directory:")); - Text latexDirectoryPath = new Text(viewModel.directoryProperty().get().toString()); + Text latexDirectoryPath = new Text(); + latexDirectoryPath.textProperty().bind(viewModel.directoryProperty().asString()); latexDirectoryPath.setStyle("-fx-font-family:monospace;-fx-font-weight: bold;"); Button latexDirectoryButton = new Button(Localization.lang("Set LaTeX file directory")); latexDirectoryButton.setGraphic(IconTheme.JabRefIcons.LATEX_FILE_DIRECTORY.getGraphicNode()); latexDirectoryButton.setOnAction(event -> viewModel.setLatexDirectory()); - Button latexDirectoryRefreshButton = new Button(Localization.lang("Refresh")); - latexDirectoryRefreshButton.setGraphic(IconTheme.JabRefIcons.REFRESH.getGraphicNode()); - latexDirectoryRefreshButton.setOnAction(event -> viewModel.refreshLatexDirectory()); - HBox latexDirectoryBox = new HBox(10, latexDirectoryText, latexDirectoryPath, latexDirectoryButton, latexDirectoryRefreshButton); + HBox latexDirectoryBox = new HBox(10, latexDirectoryText, latexDirectoryPath, latexDirectoryButton); latexDirectoryBox.setAlignment(Pos.CENTER); return latexDirectoryBox; } private VBox getCitationsPane() { VBox citationsBox = new VBox(30, citationsDisplay); + VBox.setVgrow(citationsDisplay, Priority.ALWAYS); citationsBox.setStyle("-fx-padding: 0;"); return citationsBox; } @@ -122,7 +134,8 @@ private VBox getNotFoundPane() { private VBox getErrorPane() { Label titleLabel = new Label(Localization.lang("Error")); titleLabel.setStyle("-fx-font-size: 1.5em;-fx-font-weight: bold;-fx-text-fill: -fx-accent;"); - Text errorMessageText = new Text(viewModel.searchErrorProperty().get()); + Text errorMessageText = new Text(); + errorMessageText.textProperty().bind(viewModel.searchErrorProperty()); VBox errorMessageBox = new VBox(30, titleLabel, errorMessageText); errorMessageBox.setStyle("-fx-padding: 30 0 0 30;"); return errorMessageBox; @@ -130,7 +143,7 @@ private VBox getErrorPane() { @Override protected void bindToEntry(BibEntry entry) { - viewModel.init(entry); + viewModel.bindToEntry(entry); } @Override diff --git a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java index 833ce40ef63..2549e9ec211 100644 --- a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java +++ b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java @@ -1,11 +1,15 @@ package org.jabref.gui.entryeditor; +import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; import java.util.Optional; -import java.util.concurrent.Future; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyListWrapper; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; @@ -14,23 +18,29 @@ import org.jabref.gui.AbstractViewModel; import org.jabref.gui.DialogService; -import org.jabref.gui.util.BackgroundTask; +import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.DirectoryDialogConfiguration; -import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.texparser.CitationFinder; +import org.jabref.logic.texparser.DefaultLatexParser; import org.jabref.logic.util.io.FileUtil; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.texparser.Citation; +import org.jabref.model.texparser.LatexParserResult; +import org.jabref.model.texparser.LatexParserResults; +import org.jabref.model.util.DirectoryMonitorManager; import org.jabref.preferences.PreferencesService; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.apache.commons.io.filefilter.IOFileFilter; +import org.apache.commons.io.monitor.FileAlterationListener; +import org.apache.commons.io.monitor.FileAlterationObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LatexCitationsTabViewModel extends AbstractViewModel { - enum Status { + public enum Status { IN_PROGRESS, CITATIONS_FOUND, NO_RESULTS, @@ -39,114 +49,187 @@ enum Status { private static final Logger LOGGER = LoggerFactory.getLogger(LatexCitationsTabViewModel.class); private static final String TEX_EXT = ".tex"; + private static final IOFileFilter FILE_FILTER = FileFilterUtils.or(FileFilterUtils.suffixFileFilter(TEX_EXT), FileFilterUtils.directoryFileFilter()); private final BibDatabaseContext databaseContext; private final PreferencesService preferencesService; - private final TaskExecutor taskExecutor; private final DialogService dialogService; private final ObjectProperty directory; - private CitationFinder citationFinder; private final ObservableList citationList; private final ObjectProperty status; private final StringProperty searchError; - private Future searchTask; + private final BooleanProperty updateStatusOnCreate; + private final DefaultLatexParser latexParser; + private final LatexParserResults latexFiles; + private final DirectoryMonitorManager directoryMonitorManager; + private final FileAlterationListener listener; + private FileAlterationObserver observer; private BibEntry currentEntry; public LatexCitationsTabViewModel(BibDatabaseContext databaseContext, PreferencesService preferencesService, - TaskExecutor taskExecutor, - DialogService dialogService) { + DialogService dialogService, + DirectoryMonitorManager directoryMonitorManager) { + this.databaseContext = databaseContext; this.preferencesService = preferencesService; - this.taskExecutor = taskExecutor; this.dialogService = dialogService; this.directory = new SimpleObjectProperty<>(databaseContext.getMetaData().getLatexFileDirectory(preferencesService.getFilePreferences().getUserAndHost()) .orElse(FileUtil.getInitialDirectory(databaseContext, preferencesService.getFilePreferences().getWorkingDirectory()))); - this.citationFinder = new CitationFinder(directory.get()); this.citationList = FXCollections.observableArrayList(); this.status = new SimpleObjectProperty<>(Status.IN_PROGRESS); this.searchError = new SimpleStringProperty(""); + this.directoryMonitorManager = directoryMonitorManager; + this.updateStatusOnCreate = new SimpleBooleanProperty(false); + this.listener = getListener(); + + this.latexParser = new DefaultLatexParser(); + this.latexFiles = new LatexParserResults(); } - public void init(BibEntry entry) { - cancelSearch(); + private FileAlterationListener getListener() { + return new FileAlterationListener() { + @Override + public void onStart(FileAlterationObserver observer) { + if (!updateStatusOnCreate.get()) { + status.set(Status.IN_PROGRESS); + } + } + + @Override + public void onStop(FileAlterationObserver observer) { + if (!updateStatusOnCreate.get()) { + updateStatusOnCreate.set(true); + updateStatus(); + } + } + + @Override + public void onFileCreate(File file) { + Path path = file.toPath(); + LatexParserResult result = latexParser.parse(path).get(); + latexFiles.add(path, result); + + Optional citationKey = currentEntry.getCitationKey(); + if (citationKey.isPresent()) { + Collection citations = result.getCitationsByKey(citationKey.get()); + DefaultTaskExecutor.runInJavaFXThread(() -> citationList.addAll(citations)); + } + + if (updateStatusOnCreate.get()) { + updateStatus(); + } + } + + @Override + public void onFileDelete(File file) { + LatexParserResult result = latexFiles.remove(file.toPath()); + + Optional citationKey = currentEntry.getCitationKey(); + if (citationKey.isPresent()) { + Collection citations = result.getCitationsByKey(citationKey.get()); + DefaultTaskExecutor.runInJavaFXThread(() -> citationList.removeAll(citations)); + updateStatus(); + } + } + + @Override + public void onFileChange(File file) { + onFileDelete(file); + onFileCreate(file); + updateStatus(); + } + + @Override + public void onDirectoryChange(File directory) { + } + + @Override + public void onDirectoryCreate(File directory) { + } + + @Override + public void onDirectoryDelete(File directory) { + } + }; + } + + public void bindToEntry(BibEntry entry) { + checkAndUpdateDirectory(); currentEntry = entry; - Optional citeKey = entry.getCitationKey(); + Optional citationKey = entry.getCitationKey(); + + if (observer == null) { + observer = new FileAlterationObserver(directory.get().toFile(), FILE_FILTER); + directoryMonitorManager.addObserver(observer, listener); + } - if (citeKey.isPresent()) { - startSearch(citeKey.get()); + if (citationKey.isPresent()) { + citationList.setAll(latexFiles.getCitationsByKey(citationKey.get())); + if (!status.get().equals(Status.IN_PROGRESS)) { + updateStatus(); + } } else { searchError.set(Localization.lang("Selected entry does not have an associated citation key.")); status.set(Status.ERROR); } } - public ObjectProperty directoryProperty() { - return directory; - } - - public ObservableList getCitationList() { - return new ReadOnlyListWrapper<>(citationList); - } - - public ObjectProperty statusProperty() { - return status; - } + public void setLatexDirectory() { + DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder() + .withInitialDirectory(directory.get()).build(); - public StringProperty searchErrorProperty() { - return searchError; - } + dialogService.showDirectorySelectionDialog(directoryDialogConfiguration).ifPresent(selectedDirectory -> + databaseContext.getMetaData().setLatexFileDirectory(preferencesService.getFilePreferences().getUserAndHost(), selectedDirectory.toAbsolutePath())); - private void startSearch(String citeKey) { - // we need to check whether the user meanwhile set the LaTeX file directory or the database changed locations checkAndUpdateDirectory(); - - searchTask = BackgroundTask.wrap(() -> citationFinder.searchAndParse(citeKey)) - .onRunning(() -> status.set(Status.IN_PROGRESS)) - .onSuccess(result -> { - citationList.setAll(result); - status.set(citationList.isEmpty() ? Status.NO_RESULTS : Status.CITATIONS_FOUND); - }) - .onFailure(error -> { - searchError.set(error.getMessage()); - status.set(Status.ERROR); - }) - .executeWith(taskExecutor); - } - - private void cancelSearch() { - if (searchTask == null || searchTask.isCancelled() || searchTask.isDone()) { - return; - } - - status.set(Status.IN_PROGRESS); - searchTask.cancel(true); } - public void checkAndUpdateDirectory() { + private void checkAndUpdateDirectory() { Path newDirectory = databaseContext.getMetaData().getLatexFileDirectory(preferencesService.getFilePreferences().getUserAndHost()) .orElse(FileUtil.getInitialDirectory(databaseContext, preferencesService.getFilePreferences().getWorkingDirectory())); if (!newDirectory.equals(directory.get())) { + status.set(Status.IN_PROGRESS); + updateStatusOnCreate.set(false); + citationList.clear(); + latexFiles.clear(); + + directoryMonitorManager.removeObserver(observer); directory.set(newDirectory); - citationFinder = new CitationFinder(newDirectory); + observer = new FileAlterationObserver(directory.get().toFile(), FILE_FILTER); + directoryMonitorManager.addObserver(observer, listener); } } - public void setLatexDirectory() { - DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder() - .withInitialDirectory(directory.get()).build(); + private void updateStatus() { + DefaultTaskExecutor.runInJavaFXThread(() -> { + if (!Files.exists(directory.get())) { + searchError.set(Localization.lang("Current search directory does not exist: %0", directory.get())); + status.set(Status.ERROR); + } else if (citationList.isEmpty()) { + status.set(Status.NO_RESULTS); + } else { + status.set(Status.CITATIONS_FOUND); + } + }); + } - dialogService.showDirectorySelectionDialog(directoryDialogConfiguration).ifPresent(selectedDirectory -> - databaseContext.getMetaData().setLatexFileDirectory(preferencesService.getFilePreferences().getUserAndHost(), selectedDirectory.toAbsolutePath())); + public ObjectProperty directoryProperty() { + return directory; + } + + public ObservableList getCitationList() { + return new ReadOnlyListWrapper<>(citationList); + } - init(currentEntry); + public ObjectProperty statusProperty() { + return status; } - public void refreshLatexDirectory() { - citationFinder = new CitationFinder(directory.get()); - init(currentEntry); + public StringProperty searchErrorProperty() { + return searchError; } public boolean shouldShow() { diff --git a/src/main/java/org/jabref/gui/util/DefaultDirectoryMonitor.java b/src/main/java/org/jabref/gui/util/DefaultDirectoryMonitor.java new file mode 100644 index 00000000000..522a63632b0 --- /dev/null +++ b/src/main/java/org/jabref/gui/util/DefaultDirectoryMonitor.java @@ -0,0 +1,54 @@ +package org.jabref.gui.util; + +import org.jabref.model.util.DirectoryMonitor; + +import org.apache.commons.io.monitor.FileAlterationListener; +import org.apache.commons.io.monitor.FileAlterationMonitor; +import org.apache.commons.io.monitor.FileAlterationObserver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultDirectoryMonitor implements DirectoryMonitor { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDirectoryMonitor.class); + private static final int POLL_INTERVAL = 1000; + + private final FileAlterationMonitor monitor; + + public DefaultDirectoryMonitor() { + monitor = new FileAlterationMonitor(POLL_INTERVAL); + start(); + } + + @Override + public void addObserver(FileAlterationObserver observer, FileAlterationListener listener) { + if (observer != null) { + observer.addListener(listener); + monitor.addObserver(observer); + } + } + + @Override + public void removeObserver(FileAlterationObserver observer) { + if (observer != null) { + monitor.removeObserver(observer); + } + } + + public void start() { + try { + monitor.start(); + } catch (Exception e) { + LOGGER.error("Error starting directory monitor", e); + } + } + + @Override + public void shutdown() { + try { + monitor.stop(); + } catch (Exception e) { + LOGGER.error("Error stopping directory monitor", e); + } + } +} diff --git a/src/main/java/org/jabref/logic/texparser/CitationFinder.java b/src/main/java/org/jabref/logic/texparser/CitationFinder.java deleted file mode 100644 index fac31569876..00000000000 --- a/src/main/java/org/jabref/logic/texparser/CitationFinder.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.jabref.logic.texparser; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collection; -import java.util.List; -import java.util.stream.Stream; - -import org.jabref.model.texparser.Citation; -import org.jabref.model.texparser.LatexParserResults; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CitationFinder { - - private static final Logger LOGGER = LoggerFactory.getLogger(CitationFinder.class); - private static final String TEX_EXT = ".tex"; - private final Path directory; - - private LatexParserResults latexParserResults; - - public CitationFinder(Path directory) { - this.directory = directory; - } - - public Collection searchAndParse(String citeKey) throws IOException { - if (latexParserResults == null) { - if (!Files.exists(directory)) { - throw new IOException("Current search directory does not exist: %s".formatted(directory)); - } - - List texFiles = searchDirectory(directory); - LOGGER.debug("Found tex files: {}", texFiles); - latexParserResults = new DefaultLatexParser().parse(texFiles); - } - - return latexParserResults.getCitationsByKey(citeKey); - } - - /** - * @param directory the directory to search for. It is recursively searched. - */ - private List searchDirectory(Path directory) { - LOGGER.debug("Searching directory {}", directory); - try (Stream paths = Files.walk(directory)) { - return paths.filter(Files::isRegularFile) - .filter(path -> path.toString().endsWith(TEX_EXT)) - .toList(); - } catch (IOException e) { - LOGGER.error("Error while searching files", e); - return List.of(); - } - } -} diff --git a/src/main/java/org/jabref/logic/texparser/DefaultLatexParser.java b/src/main/java/org/jabref/logic/texparser/DefaultLatexParser.java index 385c3b8b94e..1e24671c21e 100644 --- a/src/main/java/org/jabref/logic/texparser/DefaultLatexParser.java +++ b/src/main/java/org/jabref/logic/texparser/DefaultLatexParser.java @@ -12,7 +12,6 @@ import java.nio.file.Path; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -50,12 +49,6 @@ public class DefaultLatexParser implements LatexParser { private static final Pattern INCLUDE_PATTERN = Pattern.compile( "\\\\(?:include|input)\\{(?<%s>[^\\}]*)\\}".formatted(INCLUDE_GROUP)); - private final LatexParserResults latexParserResults; - - public DefaultLatexParser() { - this.latexParserResults = new LatexParserResults(); - } - @Override public LatexParserResult parse(String citeString) { Path path = Path.of(""); @@ -70,6 +63,7 @@ public Optional parse(Path latexFile) { LOGGER.error("File does not exist: {}", latexFile); return Optional.empty(); } + LatexParserResult latexParserResult = new LatexParserResult(latexFile); try (InputStream inputStream = Files.newInputStream(latexFile); @@ -99,20 +93,9 @@ public Optional parse(Path latexFile) { @Override public LatexParserResults parse(List latexFiles) { - for (Path latexFile : latexFiles) { - if (!latexParserResults.isParsed(latexFile)) { - parse(latexFile).ifPresent(parsedTex -> latexParserResults.add(latexFile, parsedTex)); - } - } - - Set nonParsedNestedFiles = latexParserResults.getNonParsedNestedFiles(); - // Parse all "non-parsed" files referenced by TEX files, recursively. - if (!nonParsedNestedFiles.isEmpty()) { - // modifies class variable latexParserResults - parse(nonParsedNestedFiles.stream().toList()); - } - - return latexParserResults; + LatexParserResults results = new LatexParserResults(); + latexFiles.forEach(file -> parse(file).ifPresent(result -> results.add(file, result))); + return results; } /** diff --git a/src/main/java/org/jabref/model/texparser/LatexParserResults.java b/src/main/java/org/jabref/model/texparser/LatexParserResults.java index 63245fea4ea..d09df07408a 100644 --- a/src/main/java/org/jabref/model/texparser/LatexParserResults.java +++ b/src/main/java/org/jabref/model/texparser/LatexParserResults.java @@ -28,31 +28,20 @@ public LatexParserResults(LatexParserResult... parsedFiles) { } } - public boolean isParsed(Path texFile) { - return parsedTexFiles.containsKey(texFile); - } - public void add(Path texFile, LatexParserResult parsedFile) { parsedTexFiles.put(texFile, parsedFile); } + public LatexParserResult remove(Path texFile) { + return parsedTexFiles.remove(texFile); + } + public Set getBibFiles() { Set bibFiles = new HashSet<>(); parsedTexFiles.values().forEach(result -> bibFiles.addAll(result.getBibFiles())); return bibFiles; } - public Set getNonParsedNestedFiles() { - Set nonParsedNestedFiles = new HashSet<>(); - for (LatexParserResult result : parsedTexFiles.values()) { - nonParsedNestedFiles.addAll(result.getNestedFiles() - .stream() - .filter(nestedFile -> !parsedTexFiles.containsKey(nestedFile)) - .toList()); - } - return nonParsedNestedFiles; - } - public Multimap getCitations() { Multimap citations = HashMultimap.create(); parsedTexFiles.forEach((path, result) -> citations.putAll(result.getCitations())); @@ -65,6 +54,10 @@ public Collection getCitationsByKey(String key) { return citations; } + public void clear() { + parsedTexFiles.clear(); + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/src/main/java/org/jabref/model/util/DirectoryMonitor.java b/src/main/java/org/jabref/model/util/DirectoryMonitor.java new file mode 100644 index 00000000000..0678704c8a1 --- /dev/null +++ b/src/main/java/org/jabref/model/util/DirectoryMonitor.java @@ -0,0 +1,25 @@ +package org.jabref.model.util; + +import org.apache.commons.io.monitor.FileAlterationListener; +import org.apache.commons.io.monitor.FileAlterationObserver; + +public interface DirectoryMonitor { + /** + * Add an observer to the monitor. + * + * @param observer The directory to observe. + * @param listener The listener to invoke when the directory changes. + */ + void addObserver(FileAlterationObserver observer, FileAlterationListener listener); + + /** + * Remove an observer from the monitor. + * + * @param observer The directory to stop monitoring. + */ + void removeObserver(FileAlterationObserver observer); + + void start(); + + void shutdown(); +} diff --git a/src/main/java/org/jabref/model/util/DirectoryMonitorManager.java b/src/main/java/org/jabref/model/util/DirectoryMonitorManager.java new file mode 100644 index 00000000000..ba3c6546a8f --- /dev/null +++ b/src/main/java/org/jabref/model/util/DirectoryMonitorManager.java @@ -0,0 +1,36 @@ +package org.jabref.model.util; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.monitor.FileAlterationListener; +import org.apache.commons.io.monitor.FileAlterationObserver; + +public class DirectoryMonitorManager { + + private final DirectoryMonitor directoryMonitor; + private final List observers = new ArrayList<>(); + + public DirectoryMonitorManager(DirectoryMonitor directoryMonitor) { + this.directoryMonitor = directoryMonitor; + } + + public void addObserver(FileAlterationObserver observer, FileAlterationListener listener) { + directoryMonitor.addObserver(observer, listener); + observers.add(observer); + } + + public void removeObserver(FileAlterationObserver observer) { + directoryMonitor.removeObserver(observer); + observers.remove(observer); + } + + /** + * Unregister all observers associated with this manager from the directory monitor. + * This method should be called when the library is closed to stop watching observers. + */ + public void unregister() { + observers.forEach(directoryMonitor::removeObserver); + observers.clear(); + } +} diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 21233b43e2f..8e7e5c82d98 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2043,10 +2043,10 @@ LaTeX\ Citations=LaTeX Citations Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Search citations for this entry in LaTeX files No\ citations\ found=No citations found No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=No LaTeX files containing this entry were found. +Current\ search\ directory\ does\ not\ exist\:\ %0= Current search directory does not exist: %0 Selected\ entry\ does\ not\ have\ an\ associated\ citation\ key.=Selected entry does not have an associated citation key. Current\ search\ directory\:=Current search directory: Set\ LaTeX\ file\ directory=Set LaTeX file directory -Refresh=Refresh Import\ entries\ from\ LaTeX\ files=Import entries from LaTeX files Import\ new\ entries=Import new entries Group\ color=Group color From a3b71c6222a2995418ecbf9baf108f17e02edbba Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 29 Apr 2024 09:30:38 +0200 Subject: [PATCH 11/54] Make newcomer message even more welcoming (#11243) --- .github/workflows/add-greeting-to-issue.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/add-greeting-to-issue.yml b/.github/workflows/add-greeting-to-issue.yml index 497a43d3697..b94650f1a62 100644 --- a/.github/workflows/add-greeting-to-issue.yml +++ b/.github/workflows/add-greeting-to-issue.yml @@ -20,6 +20,10 @@ jobs: with: issue-number: ${{ github.event.issue.number || github.event.pull_request.number }} body: | - As a general advice for newcomers: check out [Contributing](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md) for a start. Also, [guidelines for setting up a local workspace](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace) is worth having a look at. + Welcome to the vibrant world of open-source development with JabRef! - Feel free to ask here at GitHub, if you have any issue related questions. If you have questions about how to setup your workspace use JabRef's [Gitter](https://gitter.im/JabRef/jabref) chat. Try to open a (draft) pull-request early on, so that people can see you are working on the issue and so that they can see the direction the pull request is heading towards. This way, you will likely receive valuable feedback. + Newcomers, we're excited to have you on board. Start by exploring our [Contributing](https://github.com/JabRef/jabref/blob/main/CONTRIBUTING.md) guidelines, and don't forget to check out our [workspace setup guidelines](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace) to get started smoothly. + + Having any questions or issues? Feel free to ask here on GitHub. Need help setting up your local workspace? Join the conversation on [JabRef's Gitter chat](https://gitter.im/JabRef/jabref). And don't hesitate to open a (draft) pull request early on to show the direction it is heading towards. This way, you will receive valuable feedback. + + Happy coding! šŸš€ From 3de7e146f0d57c2a3d8f71d723429e27f6b3cfa9 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 29 Apr 2024 11:33:10 +0200 Subject: [PATCH 12/54] Fix artifact creation for forks (#11247) --- .github/workflows/deployment-jdk-ea.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deployment-jdk-ea.yml b/.github/workflows/deployment-jdk-ea.yml index eadf8903387..340a566df64 100644 --- a/.github/workflows/deployment-jdk-ea.yml +++ b/.github/workflows/deployment-jdk-ea.yml @@ -214,21 +214,19 @@ jobs: create-keychain: false keychain-password: jabref - name: Build runtime image and installer - if: (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" jpackage jlinkZip - name: Package application image - if: (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash run: ${{ matrix.archivePortable }} - name: Rename files - if: (matrix.os != 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') + if: (matrix.os != 'macos-latest') shell: pwsh run: | get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "${{ steps.gitversion.outputs.AssemblySemVer }}","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}"} get-childitem -Path build/distribution/* | rename-item -NewName {$_.name -replace "portable","${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-portable"} - name: Repack deb file for Debian - if: (matrix.os == 'ubuntu-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') + if: (matrix.os == 'ubuntu-latest') shell: bash run: | cd build/distribution @@ -239,7 +237,6 @@ jobs: rm debian-binary control.tar.* data.tar.* mv -f jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64_repackaged.deb jabref_${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}_amd64.deb - name: Rename files with JDK version - if: (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash run: | for file in build/distribution/*.*; do From 529c201d27b3a74a3b0a85169f51e68208b7b244 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb <52158423+LoayGhreeb@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:38:11 +0300 Subject: [PATCH 13/54] Hide the side pane after closing all panels within it (#11241) * Add InvalidationListener to sidePane * Fix missing width listener --------- Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --- src/main/java/org/jabref/gui/frame/JabRefFrame.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/frame/JabRefFrame.java b/src/main/java/org/jabref/gui/frame/JabRefFrame.java index 84a34f53966..e154ae6f7e6 100644 --- a/src/main/java/org/jabref/gui/frame/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/frame/JabRefFrame.java @@ -9,6 +9,7 @@ import java.util.function.Supplier; import javafx.application.Platform; +import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; import javafx.beans.binding.StringBinding; import javafx.collections.transformation.FilteredList; @@ -212,7 +213,9 @@ private void initLayout() { splitPane.getItems().addAll(tabbedPane); SplitPane.setResizableWithParent(sidePane, false); - EasyBind.subscribe(sidePane.widthProperty(), c -> updateSidePane()); + sidePane.widthProperty().addListener(c -> updateSidePane()); + sidePane.getChildren().addListener((InvalidationListener) c -> updateSidePane()); + updateSidePane(); setCenter(splitPane); } From 9f2418bbed23802a4c828a934d8cbef5bf5204b4 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 29 Apr 2024 11:41:48 +0200 Subject: [PATCH 14/54] Remove download when html file (#11227) * Remove download when html file Fixes #10149 * changelog * remove test * Fix behavior when file is duplicate file * Refine comments * Keep html file when downloading inside the entry editor * Add some dots --------- Co-authored-by: Oliver Kopp --- CHANGELOG.md | 7 +- .../jabref/gui/entryeditor/EntryEditor.java | 2 + .../externalfiles/DownloadFullTextAction.java | 3 +- .../gui/externalfiles/ImportHandler.java | 6 +- .../gui/fieldeditors/LinkedFileViewModel.java | 16 ++-- .../gui/fieldeditors/LinkedFilesEditor.java | 2 +- .../LinkedFilesEditorViewModel.java | 2 +- .../linkedfile/AttachFileFromURLAction.java | 2 +- .../linkedfile/DownloadLinkedFileAction.java | 83 ++++++++++++------- .../RedownloadMissingFilesAction.java | 5 +- .../logic/util/io/FileNameUniqueness.java | 13 ++- .../java/org/jabref/model/entry/BibEntry.java | 6 +- src/main/resources/l10n/JabRef_en.properties | 4 +- .../fieldeditors/LinkedFileViewModelTest.java | 58 +++++-------- .../DownloadLinkedFileActionTest.java | 67 ++++++++++++++- 15 files changed, 177 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 134ec7736be..a2ffda77a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,14 +23,15 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv ### Changed - We replaced the word "Key bindings" with "Keyboard shortcuts" in the Preferences tab. [#11153](https://github.com/JabRef/jabref/pull/11153) -- We slightly improved the duplicate check if ISBN numbers are present. [#8885](https://github.com/JabRef/jabref/issues/8885) +- We slightly improved the duplicate check if ISBNs are present. [#8885](https://github.com/JabRef/jabref/issues/8885) +- JabRef no longer downloads HTML files of websites when a PDF was not found. [#10149](https://github.com/JabRef/jabref/issues/10149) ### Fixed -- We fixed an issue where entry type with duplicate fields prevented opening existing libraries with custom entry types [#11127](https://github.com/JabRef/jabref/issues/11127) +- We fixed an issue where entry type with duplicate fields prevented opening existing libraries with custom entry types. [#11127](https://github.com/JabRef/jabref/issues/11127) - We fixed an issue where Markdown rendering removed braces from the text. [#10928](https://github.com/JabRef/jabref/issues/10928) - We fixed an issue when the file was flagged as changed on disk in the case of content selectors or groups. [#9064](https://github.com/JabRef/jabref/issues/9064) -- We fixed crash on opening the entry editor when auto completion is enabled. [#11188](https://github.com/JabRef/jabref/issues/11188) +- We fixed crash on opening the entry editor when auto-completion is enabled. [#11188](https://github.com/JabRef/jabref/issues/11188) - We fixed the usage of the key binding for "Clear search" (default: Escape). [#10764](https://github.com/JabRef/jabref/issues/10764) - We fixed an issue where library shown as unsaved and marked (*) after accepting changes made externally to the file. [#11027](https://github.com/JabRef/jabref/issues/11027) diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java index 220d11e2299..759c46fc0ad 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditor.java @@ -74,6 +74,8 @@ *

* EntryEditor also registers itself to the event bus, receiving events whenever a field of the entry changes, enabling * the text fields to update themselves if the change is made from somewhere else. + *

+ * The editors for fields are created via {@link org.jabref.gui.fieldeditors.FieldEditors}. */ public class EntryEditor extends BorderPane { diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java index 321f31bc31f..7197f9c2422 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java @@ -141,8 +141,7 @@ private void addLinkedFileFromURL(BibDatabaseContext databaseContext, URL url, B taskExecutor, dialogService, preferences); - - onlineFile.download(); + onlineFile.download(true); } else { dialogService.notify(Localization.lang("Full text document for entry %0 already linked.", entry.getCitationKey().orElse(Localization.lang("undefined")))); diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index de4ac03f686..8465c4305f6 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -276,7 +276,7 @@ public void downloadLinkedFiles(BibEntry entry) { taskExecutor, dialogService, preferencesService - ).download() + ).download(false) ); } } @@ -405,13 +405,13 @@ public void importEntriesWithDuplicateCheck(BibDatabaseContext database, List linkedFile.edit(); case OPEN_FILE -> linkedFile.open(); case OPEN_FOLDER -> linkedFile.openFolder(); - case DOWNLOAD_FILE -> linkedFile.download(); + case DOWNLOAD_FILE -> linkedFile.download(true); case REDOWNLOAD_FILE -> linkedFile.redownload(); case RENAME_FILE_TO_PATTERN -> linkedFile.renameToSuggestion(); case RENAME_FILE_TO_NAME -> linkedFile.askForNameAndRename(); diff --git a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java index 91d0dbf6690..005ef6e75ff 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java +++ b/src/main/java/org/jabref/gui/fieldeditors/LinkedFilesEditorViewModel.java @@ -279,7 +279,7 @@ private void addFromURLAndDownload(URL url) { dialogService, preferences); files.add(onlineFile); - onlineFile.download(); + onlineFile.download(true); } public void deleteFile(LinkedFileViewModel file) { diff --git a/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java b/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java index 08c9240b0a2..e95af5e25a6 100644 --- a/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java +++ b/src/main/java/org/jabref/gui/linkedfile/AttachFileFromURLAction.java @@ -68,7 +68,7 @@ public void execute() { taskExecutor, dialogService, preferencesService); - onlineFile.download(); + onlineFile.download(true); } catch (MalformedURLException exception) { dialogService.showErrorDialogAndWait(Localization.lang("Invalid URL"), exception); } diff --git a/src/main/java/org/jabref/gui/linkedfile/DownloadLinkedFileAction.java b/src/main/java/org/jabref/gui/linkedfile/DownloadLinkedFileAction.java index 53684fe43f7..6df76576628 100644 --- a/src/main/java/org/jabref/gui/linkedfile/DownloadLinkedFileAction.java +++ b/src/main/java/org/jabref/gui/linkedfile/DownloadLinkedFileAction.java @@ -6,6 +6,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import javax.net.ssl.HostnameVerifier; @@ -55,6 +57,7 @@ public class DownloadLinkedFileAction extends SimpleCommand { private final String downloadUrl; private final FilePreferences filePreferences; private final TaskExecutor taskExecutor; + private final boolean keepHtmlLink; private final BibDatabaseContext databaseContext; @@ -68,7 +71,8 @@ public DownloadLinkedFileAction(BibDatabaseContext databaseContext, DialogService dialogService, FilePreferences filePreferences, TaskExecutor taskExecutor, - String suggestedName) { + String suggestedName, + boolean keepHtmlLink) { this.databaseContext = databaseContext; this.entry = entry; this.linkedFile = linkedFile; @@ -77,10 +81,14 @@ public DownloadLinkedFileAction(BibDatabaseContext databaseContext, this.dialogService = dialogService; this.filePreferences = filePreferences; this.taskExecutor = taskExecutor; + this.keepHtmlLink = keepHtmlLink; this.linkedFileHandler = new LinkedFileHandler(linkedFile, entry, databaseContext, filePreferences); } + /** + * Downloads the given linked file to the first existing file directory. It keeps HTML files as URLs. + */ public DownloadLinkedFileAction(BibDatabaseContext databaseContext, BibEntry entry, LinkedFile linkedFile, @@ -88,12 +96,12 @@ public DownloadLinkedFileAction(BibDatabaseContext databaseContext, DialogService dialogService, FilePreferences filePreferences, TaskExecutor taskExecutor) { - this(databaseContext, entry, linkedFile, downloadUrl, dialogService, filePreferences, taskExecutor, ""); + this(databaseContext, entry, linkedFile, downloadUrl, dialogService, filePreferences, taskExecutor, "", true); } @Override public void execute() { - LOGGER.info("Downloading file from " + downloadUrl); + LOGGER.info("Downloading file from {}", downloadUrl); if (downloadUrl.isEmpty() || !LinkedFile.isOnlineLink(downloadUrl)) { throw new UnsupportedOperationException("In order to download the file, the url has to be an online link"); } @@ -132,51 +140,64 @@ private void doDownload(Path targetDirectory, URLDownload urlDownload) { taskExecutor.execute(downloadTask); } - private void onSuccess(Path targetDirectory, Path destination) { + /** + * @param targetDirectory The directory to store the file into. Is an absolute path. + */ + private void onSuccess(Path targetDirectory, Path downloadedFile) { + assert targetDirectory.isAbsolute(); + boolean isDuplicate; boolean isHtml; try { - isDuplicate = FileNameUniqueness.isDuplicatedFile(targetDirectory, destination.getFileName(), dialogService); + isDuplicate = FileNameUniqueness.isDuplicatedFile(targetDirectory, downloadedFile.getFileName(), dialogService); } catch (IOException e) { LOGGER.error("FileNameUniqueness.isDuplicatedFile failed", e); return; } if (isDuplicate) { - destination = targetDirectory.resolve( - FileNameUniqueness.eraseDuplicateMarks(destination.getFileName())); - - linkedFile.setLink(FileUtil.relativize(destination, - databaseContext.getFileDirectories(filePreferences)).toString()); + // We do not add duplicate files. + // The downloaded file was deleted in {@link org.jabref.logic.util.io.FileNameUniqueness.isDuplicatedFile]} + LOGGER.info("File {} already exists in target directory {}.", downloadedFile.getFileName(), targetDirectory); + return; + } - isHtml = linkedFile.getFileType().equals(StandardExternalFileType.URL.getName()); + // we need to call LinkedFileViewModel#fromFile, because we need to make the path relative to the configured directories + LinkedFile newLinkedFile = LinkedFilesEditorViewModel.fromFile( + downloadedFile, + databaseContext.getFileDirectories(filePreferences), + filePreferences); + if (newLinkedFile.getDescription().isEmpty() && !linkedFile.getDescription().isEmpty()) { + newLinkedFile.setDescription((linkedFile.getDescription())); + } + if (linkedFile.getSourceUrl().isEmpty() && LinkedFile.isOnlineLink(linkedFile.getLink())) { + newLinkedFile.setSourceURL(linkedFile.getLink()); } else { - // we need to call LinkedFileViewModel#fromFile, because we need to make the path relative to the configured directories - LinkedFile newLinkedFile = LinkedFilesEditorViewModel.fromFile( - destination, - databaseContext.getFileDirectories(filePreferences), - filePreferences); - if (newLinkedFile.getDescription().isEmpty() && !linkedFile.getDescription().isEmpty()) { - newLinkedFile.setDescription((linkedFile.getDescription())); - } - if (linkedFile.getSourceUrl().isEmpty() && LinkedFile.isOnlineLink(linkedFile.getLink())) { - newLinkedFile.setSourceURL(linkedFile.getLink()); - } else { - newLinkedFile.setSourceURL(linkedFile.getSourceUrl()); - } - entry.replaceDownloadedFile(linkedFile.getLink(), newLinkedFile); - isHtml = newLinkedFile.getFileType().equals(StandardExternalFileType.URL.getName()); + newLinkedFile.setSourceURL(linkedFile.getSourceUrl()); } - // Notify in bar when the file type is HTML. + isHtml = newLinkedFile.getFileType().equals(StandardExternalFileType.URL.getName()); if (isHtml) { - dialogService.notify(Localization.lang("Downloaded website as an HTML file.")); - LOGGER.debug("Downloaded website {} as an HTML file at {}", linkedFile.getLink(), destination); + if (this.keepHtmlLink) { + dialogService.notify(Localization.lang("Download '%0' was a HTML file. Keeping URL.", downloadUrl)); + } else { + dialogService.notify(Localization.lang("Download '%0' was a HTML file. Removed.", downloadUrl)); + List newFiles = new ArrayList<>(entry.getFiles()); + newFiles.remove(linkedFile); + entry.setFiles(newFiles); + try { + Files.delete(downloadedFile); + } catch (IOException e) { + LOGGER.error("Could not delete downloaded file {}.", downloadedFile, e); + } + } + } else { + entry.replaceDownloadedFile(linkedFile.getLink(), newLinkedFile); } } private void onFailure(URLDownload urlDownload, Exception ex) { - LOGGER.error("Error downloading from URL: " + urlDownload, ex); + LOGGER.error("Error downloading from URL: {}", urlDownload, ex); String fetcherExceptionMessage = ex.getMessage(); String failedTitle = Localization.lang("Failed to download from URL"); int statusCode; @@ -261,7 +282,7 @@ private Optional inferFileTypeFromMimeType(URLDownload urlDown String mimeType = urlDownload.getMimeType(); if (mimeType != null) { - LOGGER.debug("MIME Type suggested: " + mimeType); + LOGGER.debug("MIME Type suggested: {}", mimeType); return ExternalFileTypes.getExternalFileTypeByMimeType(mimeType, filePreferences); } else { return Optional.empty(); diff --git a/src/main/java/org/jabref/gui/linkedfile/RedownloadMissingFilesAction.java b/src/main/java/org/jabref/gui/linkedfile/RedownloadMissingFilesAction.java index f08933976cb..3a69061b10f 100644 --- a/src/main/java/org/jabref/gui/linkedfile/RedownloadMissingFilesAction.java +++ b/src/main/java/org/jabref/gui/linkedfile/RedownloadMissingFilesAction.java @@ -56,6 +56,9 @@ public void execute() { } } + /** + * @implNote Similar method {@link org.jabref.gui.fieldeditors.LinkedFileViewModel#redownload} + */ private void redownloadMissing(BibDatabaseContext databaseContext) { LOGGER.info("Redownloading missing files"); databaseContext.getEntries().forEach(entry -> { @@ -71,7 +74,7 @@ private void redownloadMissing(BibDatabaseContext databaseContext) { String fileName = Path.of(linkedFile.getLink()).getFileName().toString(); DownloadLinkedFileAction downloadAction = new DownloadLinkedFileAction(this.databaseContext, entry, - linkedFile, linkedFile.getSourceUrl(), dialogService, filePreferences, taskExecutor, fileName); + linkedFile, linkedFile.getSourceUrl(), dialogService, filePreferences, taskExecutor, fileName, true); downloadAction.execute(); }); }); diff --git a/src/main/java/org/jabref/logic/util/io/FileNameUniqueness.java b/src/main/java/org/jabref/logic/util/io/FileNameUniqueness.java index 2c1456d13cd..44a73e0ae71 100644 --- a/src/main/java/org/jabref/logic/util/io/FileNameUniqueness.java +++ b/src/main/java/org/jabref/logic/util/io/FileNameUniqueness.java @@ -11,7 +11,12 @@ import org.jabref.gui.DialogService; import org.jabref.logic.l10n.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class FileNameUniqueness { + private static final Logger LOGGER = LoggerFactory.getLogger(FileNameUniqueness.class); + private static final Pattern DUPLICATE_MARK_PATTERN = Pattern.compile("(.*) \\(\\d+\\)"); /** @@ -61,7 +66,6 @@ public static String getNonOverWritingFileName(Path targetDirectory, String file * @throws IOException Fail when the file is not exist or something wrong when reading the file */ public static boolean isDuplicatedFile(Path directory, Path fileName, DialogService dialogService) throws IOException { - Objects.requireNonNull(directory); Objects.requireNonNull(fileName); Objects.requireNonNull(dialogService); @@ -83,10 +87,11 @@ public static boolean isDuplicatedFile(Path directory, Path fileName, DialogServ while (Files.exists(originalFile)) { if (com.google.common.io.Files.equal(originalFile.toFile(), duplicateFile.toFile())) { - if (duplicateFile.toFile().delete()) { + try { + Files.delete(duplicateFile); dialogService.notify(Localization.lang("File '%1' is a duplicate of '%0'. Keeping '%0'", originalFileName, fileName)); - } else { - dialogService.notify(Localization.lang("File '%1' is a duplicate of '%0'. Keeping both due to deletion error", originalFileName, fileName)); + } catch (IOException e) { + LOGGER.error("File '{}' is a duplicate of '{}'. Could not delete '{}'.", fileName, originalFileName, fileName); } return true; } diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index 19b33f257fb..6f463422a36 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -613,11 +613,11 @@ public Optional setField(Field field, String value, EntriesEventSou } String oldValue = getField(field).orElse(null); - boolean isNewField = oldValue == null; if (value.equals(oldValue)) { return Optional.empty(); } + boolean isNewField = oldValue == null; changed = true; invalidateFieldCache(field); @@ -1062,15 +1062,15 @@ public BibEntry withFiles(List files) { * Gets a list of linked files. * * @return the list of linked files, is never null but can be empty. - * Changes to the underlying list will have no effect on the entry itself. Use {@link #addFile(LinkedFile)} + * Changes to the underlying list will have no effect on the entry itself. Use {@link #addFile(LinkedFile)}. */ public List getFiles() { - // Extract the path Optional oldValue = getField(StandardField.FILE); if (oldValue.isEmpty()) { return new ArrayList<>(); // Return new ArrayList because emptyList is immutable } + // Extract the path return FileFieldParser.parse(oldValue.get()); } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 8e7e5c82d98..fdb5076f8c5 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -276,7 +276,8 @@ Donate\ to\ JabRef=Donate to JabRef Download\ file=Download file -Downloaded\ website\ as\ an\ HTML\ file.=Downloaded website as an HTML file. +Download\ '%0'\ was\ a\ HTML\ file.\ Removed.=Download '%0' was a HTML file. Removed. +Download\ '%0'\ was\ a\ HTML\ file.\ Keeping\ URL.=Download '%0' was a HTML file. Keeping URL. duplicate\ removal=duplicate removal @@ -2402,7 +2403,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=Convert timestamp field New\ entry\ by\ type=New entry by type File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=File '%1' is a duplicate of '%0'. Keeping '%0' -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=File '%1' is a duplicate of '%0'. Keeping both due to deletion error Enable\ field\ formatters=Enable field formatters Entry\ Type=Entry Type diff --git a/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java b/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java index 78b765ec3f6..18efb37fd94 100644 --- a/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java +++ b/src/test/java/org/jabref/gui/fieldeditors/LinkedFileViewModelTest.java @@ -29,7 +29,6 @@ import org.jabref.model.entry.LinkedFile; import org.jabref.preferences.FilePreferences; import org.jabref.preferences.PreferencesService; -import org.jabref.testutils.category.FetcherTest; import org.jabref.testutils.category.GUITest; import org.junit.jupiter.api.AfterEach; @@ -37,6 +36,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testfx.framework.junit5.ApplicationExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -181,8 +183,12 @@ void deleteWhenDialogCancelledReturnsFalseAndDoesNotRemoveFile() { assertTrue(Files.exists(tempFile)); } - @Test - void downloadHtmlFileCausesWarningDisplay() throws MalformedURLException { + @ParameterizedTest + @CsvSource({ + "true, Download 'https://www.google.com/' was a HTML file. Keeping URL.", + "false, Download 'https://www.google.com/' was a HTML file. Removed." + }) + void downloadHtmlFileCausesWarningDisplay(Boolean keepHtmlLink, String warningText) throws MalformedURLException { when(filePreferences.shouldStoreFilesRelativeToBibFile()).thenReturn(true); when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); when(filePreferences.getFileDirectoryPattern()).thenReturn("[entrytype]"); @@ -194,39 +200,9 @@ void downloadHtmlFileCausesWarningDisplay() throws MalformedURLException { LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, new CurrentThreadTaskExecutor(), dialogService, preferences); - viewModel.download(); + viewModel.download(keepHtmlLink); - verify(dialogService, atLeastOnce()).notify("Downloaded website as an HTML file."); - } - - @FetcherTest - @Test - void downloadHtmlWhenLinkedFilePointsToHtml() throws MalformedURLException { - // use google as test url, wiley is protected by CloudFlare - String url = "https://google.com"; - String fileType = StandardExternalFileType.URL.getName(); - linkedFile = new LinkedFile(new URL(url), fileType); - - when(filePreferences.shouldStoreFilesRelativeToBibFile()).thenReturn(true); - when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); - when(filePreferences.getFileDirectoryPattern()).thenReturn("[entrytype]"); - - databaseContext.setDatabasePath(tempFile); - - LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, new CurrentThreadTaskExecutor(), dialogService, preferences); - - viewModel.download(); - - List linkedFiles = entry.getFiles(); - - for (LinkedFile file: linkedFiles) { - if ("Misc/asdf.html".equalsIgnoreCase(file.getLink())) { - assertEquals("URL", file.getFileType()); - return; - } - } - // If the file was not found among the linked files to the entry - fail(); + verify(dialogService, atLeastOnce()).notify(warningText); } @Test @@ -290,9 +266,11 @@ void mimeTypeStringWithParameterIsReturnedAsWithoutParameter() { assertEquals("URL", actual); } - @Test - @FetcherTest - void downloadPdfFileWhenLinkedFilePointsToPdfUrl() throws MalformedURLException { + // We cannot use "@FetcherTest" annotation, because a @FetcherTest does not fire up a GUI environment (which is needed for this test) + // @FetcherTest + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void downloadPdfFileWhenLinkedFilePointsToPdfUrl(boolean keepHtml) throws MalformedURLException { linkedFile = new LinkedFile(new URL("http://arxiv.org/pdf/1207.0408v1"), "pdf"); // Needed Mockito stubbing methods to run test when(filePreferences.shouldStoreFilesRelativeToBibFile()).thenReturn(true); @@ -302,7 +280,9 @@ void downloadPdfFileWhenLinkedFilePointsToPdfUrl() throws MalformedURLException databaseContext.setDatabasePath(tempFile); LinkedFileViewModel viewModel = new LinkedFileViewModel(linkedFile, entry, databaseContext, new CurrentThreadTaskExecutor(), dialogService, preferences); - viewModel.download(); + + // TODO: Rewrite using WireMock + viewModel.download(keepHtml); // Loop through downloaded files to check for filetype='pdf' List linkedFiles = entry.getFiles(); diff --git a/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java b/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java index 80791f86bba..067b21f0cc2 100644 --- a/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java +++ b/src/test/java/org/jabref/gui/linkedfile/DownloadLinkedFileActionTest.java @@ -26,6 +26,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -33,6 +35,11 @@ import static org.mockito.Mockito.when; class DownloadLinkedFileActionTest { + + // Required for keepsHtmlEntry + @TempDir + Path tempFolder; + private BibEntry entry; private final BibDatabaseContext databaseContext = mock(BibDatabaseContext.class); @@ -84,8 +91,9 @@ void replacesLinkedFiles(@TempDir Path tempFolder) throws Exception { assertEquals(List.of(new LinkedFile("", tempFolder.resolve("asdf.pdf"), "PDF", url)), entry.getFiles()); } - @Test - void doesntReplaceSourceURL(@TempDir Path tempFolder) throws Exception { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void doesntReplaceSourceURL(boolean keepHtml) throws Exception { String url = "http://arxiv.org/pdf/1207.0408v1"; LinkedFile linkedFile = new LinkedFile(new URL(url), ""); @@ -120,9 +128,62 @@ void doesntReplaceSourceURL(@TempDir Path tempFolder) throws Exception { dialogService, preferences.getFilePreferences(), new CurrentThreadTaskExecutor(), - Path.of(linkedFile.getLink()).getFileName().toString()); + Path.of(linkedFile.getLink()).getFileName().toString(), + keepHtml); downloadLinkedFileAction2.execute(); assertEquals(List.of(new LinkedFile("", tempFolder.resolve("asdf.pdf"), "PDF", url)), entry.getFiles()); } + + @Test + void keepsHtmlEntry(@TempDir Path tempFolder) throws Exception { + String url = "https://blog.fefe.de/?ts=98e04151"; + + LinkedFile linkedFile = new LinkedFile(new URL(url), ""); + when(databaseContext.getFirstExistingFileDir(any())).thenReturn(Optional.of(tempFolder)); + when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); + when(filePreferences.getFileDirectoryPattern()).thenReturn(""); + + entry.setFiles(List.of(linkedFile)); + + BibEntry expected = (BibEntry) entry.clone(); + + DownloadLinkedFileAction downloadLinkedFileAction = new DownloadLinkedFileAction( + databaseContext, + entry, + linkedFile, + linkedFile.getLink(), + dialogService, + preferences.getFilePreferences(), + new CurrentThreadTaskExecutor()); + downloadLinkedFileAction.execute(); + + assertEquals(expected, entry); + } + + @Test + void removesHtmlEntry(@TempDir Path tempFolder) throws Exception { + String url = "https://blog.fefe.de/?ts=98e04151"; + + LinkedFile linkedFile = new LinkedFile(new URL(url), ""); + when(databaseContext.getFirstExistingFileDir(any())).thenReturn(Optional.of(tempFolder)); + when(filePreferences.getFileNamePattern()).thenReturn("[citationkey]"); + when(filePreferences.getFileDirectoryPattern()).thenReturn(""); + + entry.setFiles(List.of(linkedFile)); + + DownloadLinkedFileAction downloadLinkedFileAction = new DownloadLinkedFileAction( + databaseContext, + entry, + linkedFile, + linkedFile.getLink(), + dialogService, + preferences.getFilePreferences(), + new CurrentThreadTaskExecutor(), + "", + false); + downloadLinkedFileAction.execute(); + + assertEquals(new BibEntry().withCitationKey("asdf"), entry); + } } From 04479ca2fd5c07b724a92ad5bedbb814e42b0108 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:37:18 +0000 Subject: [PATCH 15/54] Bump src/main/resources/csl-styles from `af6f50b` to `32348cb` (#11258) Bumps [src/main/resources/csl-styles](https://github.com/citation-style-language/styles) from `af6f50b` to `32348cb`. - [Release notes](https://github.com/citation-style-language/styles/releases) - [Commits](https://github.com/citation-style-language/styles/compare/af6f50b080190ccc7ba0c83906fe36c5ece85ce5...32348cb790b809081a0ff42ce81f6bcba050e4de) --- updated-dependencies: - dependency-name: src/main/resources/csl-styles dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index af6f50b0801..32348cb790b 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit af6f50b080190ccc7ba0c83906fe36c5ece85ce5 +Subproject commit 32348cb790b809081a0ff42ce81f6bcba050e4de From a3e6b19d9419018d5353a8bd0c9c28f0f112ab03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:55:20 +0000 Subject: [PATCH 16/54] Bump com.dlsc.gemsfx:gemsfx from 2.9.0 to 2.10.0 (#11259) Bumps [com.dlsc.gemsfx:gemsfx](https://github.com/dlsc-software-consulting-gmbh/GemsFX) from 2.9.0 to 2.10.0. - [Release notes](https://github.com/dlsc-software-consulting-gmbh/GemsFX/releases) - [Changelog](https://github.com/dlsc-software-consulting-gmbh/GemsFX/blob/master/jreleaser.yml) - [Commits](https://github.com/dlsc-software-consulting-gmbh/GemsFX/compare/v2.9.0...v2.10.0) --- updated-dependencies: - dependency-name: com.dlsc.gemsfx:gemsfx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 28db2f20df6..472fbfdafd5 100644 --- a/build.gradle +++ b/build.gradle @@ -218,7 +218,7 @@ dependencies { } implementation 'org.fxmisc.flowless:flowless:0.7.2' implementation 'org.fxmisc.richtext:richtextfx:0.11.2' - implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.9.0') { + implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.10.0') { exclude module: 'javax.inject' // Split package, use only jakarta.inject exclude module: 'commons-lang3' exclude group: 'org.openjfx' From adf1e37043b91b000fd2924caafe28a29e341188 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:56:15 +0000 Subject: [PATCH 17/54] Bump com.puppycrawl.tools:checkstyle from 10.15.0 to 10.16.0 (#11260) Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.15.0 to 10.16.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.15.0...checkstyle-10.16.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 472fbfdafd5..75acf92e208 100644 --- a/build.gradle +++ b/build.gradle @@ -311,7 +311,7 @@ dependencies { testImplementation "org.testfx:testfx-junit5:4.0.16-alpha" testImplementation "org.hamcrest:hamcrest-library:2.2" - checkstyle 'com.puppycrawl.tools:checkstyle:10.15.0' + checkstyle 'com.puppycrawl.tools:checkstyle:10.16.0' // xjc needs the runtime as well for the ant task, otherwise it fails xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' From 0adea6553be66e0d7e31210562e32edc8b6191a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:57:56 +0000 Subject: [PATCH 18/54] Bump org.xmlunit:xmlunit-core from 2.9.1 to 2.10.0 (#11261) Bumps [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit) from 2.9.1 to 2.10.0. - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.9.1...v2.10.0) --- updated-dependencies: - dependency-name: org.xmlunit:xmlunit-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 75acf92e208..6e9e2de3b67 100644 --- a/build.gradle +++ b/build.gradle @@ -303,7 +303,7 @@ dependencies { testImplementation 'org.junit.platform:junit-platform-launcher:1.10.2' testImplementation 'org.mockito:mockito-core:5.11.0' - testImplementation 'org.xmlunit:xmlunit-core:2.9.1' + testImplementation 'org.xmlunit:xmlunit-core:2.10.0' testImplementation 'org.xmlunit:xmlunit-matchers:2.9.1' testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.3.0' testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.3.0' From 90acb03a6c0533ee04e8f794bc9168848f882e99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:59:45 +0000 Subject: [PATCH 19/54] Bump org.openrewrite.rewrite from 6.11.2 to 6.12.0 (#11262) Bumps org.openrewrite.rewrite from 6.11.2 to 6.12.0. --- updated-dependencies: - dependency-name: org.openrewrite.rewrite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6e9e2de3b67..77dd961a65c 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ plugins { id 'idea' - id 'org.openrewrite.rewrite' version '6.11.2' + id 'org.openrewrite.rewrite' version '6.12.0' } // Enable following for debugging From 5cff1ad7615728924b6f30ad37e9f9edc7092694 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:06:14 +0000 Subject: [PATCH 20/54] Bump lycheeverse/lychee-action from 1.9.3 to 1.10.0 (#11264) Bumps [lycheeverse/lychee-action](https://github.com/lycheeverse/lychee-action) from 1.9.3 to 1.10.0. - [Release notes](https://github.com/lycheeverse/lychee-action/releases) - [Commits](https://github.com/lycheeverse/lychee-action/compare/v1.9.3...v1.10.0) --- updated-dependencies: - dependency-name: lycheeverse/lychee-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-links.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index bad9c1a6943..ccb056e9bec 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -31,7 +31,7 @@ jobs: restore-keys: cache-lychee- - name: Link Checker id: lychee - uses: lycheeverse/lychee-action@v1.9.3 + uses: lycheeverse/lychee-action@v1.10.0 with: fail: true args: --accept '200,201,202,203,204,403,429,500' --max-concurrency 1 --cache --no-progress --exclude-all-private './**/*.md' From a66e76097070f3763987f29ce1d11fec74af26b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:09:52 +0000 Subject: [PATCH 21/54] Bump actions/github-script from 6 to 7 (#11265) Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 34e34a5086c..9f2625a7228 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -340,7 +340,7 @@ jobs: runs-on: ubuntu-latest steps: - if: github.head_ref == 'main' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | core.setFailed('Pull requests should come from a branch other than "main"\n\nšŸ‘‰ Please read https://devdocs.jabref.org/contributing again carefully. šŸ‘ˆ') From 4b1f04b94c190e484927127369ed2b1005c3d8ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:25:04 +0000 Subject: [PATCH 22/54] Bump org.xmlunit:xmlunit-matchers from 2.9.1 to 2.10.0 (#11263) Bumps [org.xmlunit:xmlunit-matchers](https://github.com/xmlunit/xmlunit) from 2.9.1 to 2.10.0. - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.9.1...v2.10.0) --- updated-dependencies: - dependency-name: org.xmlunit:xmlunit-matchers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 77dd961a65c..11ed7431454 100644 --- a/build.gradle +++ b/build.gradle @@ -304,7 +304,7 @@ dependencies { testImplementation 'org.mockito:mockito-core:5.11.0' testImplementation 'org.xmlunit:xmlunit-core:2.10.0' - testImplementation 'org.xmlunit:xmlunit-matchers:2.9.1' + testImplementation 'org.xmlunit:xmlunit-matchers:2.10.0' testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.3.0' testImplementation 'com.tngtech.archunit:archunit-junit5-api:1.3.0' testImplementation "org.testfx:testfx-core:4.0.16-alpha" From 3abdefd5050a291f89ebe47b9c5218750c459348 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 29 Apr 2024 18:32:01 +0200 Subject: [PATCH 23/54] Fix Biodiversity fetcher test results (#11257) * Fix Biodiversity fetcher test results * Make CiteSeer test more relaxed when CiteSeer is not available * Fix CrossRefTest * Fix import * "Fix" IEEETest * Fix ISIDORE Fetcher Test * Disable ScholarArchiveFetcher * Disable more tests * Disable Grobid --- .../fetcher/BiodiversityLibraryTest.java | 2 +- .../logic/importer/fetcher/CiteSeerTest.java | 7 +++ .../logic/importer/fetcher/CrossRefTest.java | 1 - .../logic/importer/fetcher/IEEETest.java | 53 +++++++++++++------ .../importer/fetcher/ISIDOREFetcherTest.java | 4 +- .../fetcher/ScholarArchiveFetcherTest.java | 18 +++---- .../importer/fetcher/SemanticScholarTest.java | 17 +++--- .../fileformat/PdfGrobidImporterTest.java | 3 ++ 8 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/test/java/org/jabref/logic/importer/fetcher/BiodiversityLibraryTest.java b/src/test/java/org/jabref/logic/importer/fetcher/BiodiversityLibraryTest.java index b5d4fb4f584..b4cf58532f9 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/BiodiversityLibraryTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/BiodiversityLibraryTest.java @@ -100,7 +100,7 @@ public void performSearch() throws FetcherException { .withField(StandardField.URL, "https://www.biodiversitylibrary.org/part/356490") .withField(StandardField.DATE, "2023") .withField(StandardField.VOLUME, "227") - .withField(StandardField.PAGES, "89-97") + .withField(StandardField.PAGES, "89--97") .withField(StandardField.DOI, "10.3897/phytokeys.227.104703"); assertEquals(expected, fetcher.performSearch("Amanoa condorensis (Phyllanthaceae)").getFirst()); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java b/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java index 647c18d789d..8b5b4f125cd 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java @@ -13,18 +13,25 @@ import org.jabref.model.entry.types.StandardEntryType; import org.jabref.testutils.category.FetcherTest; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assumptions.assumeFalse; @FetcherTest class CiteSeerTest { private CiteSeer fetcher = new CiteSeer(); + @BeforeAll + static void ensureCiteSeerIsAvailable() throws Exception { + assumeFalse(List.of().equals(new CiteSeer().performSearch("title:\"Rigorous Derivation from Landau-de Gennes Theory to Ericksen-leslie Theory\" AND pageSize:1"))); + } + @Test void searchByQueryFindsEntryRigorousDerivation() throws Exception { String title = "RIGOROUS DERIVATION FROM LANDAU-DE GENNES THEORY TO ERICKSEN-LESLIE THEORY"; diff --git a/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java b/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java index 287b65e857d..d67744df55d 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CrossRefTest.java @@ -132,7 +132,6 @@ public void performSearchByIdFindsPaperWithoutTitle() throws Exception { entry.setField(StandardField.JOURNAL, "Indo-Iranian Journal"); entry.setField(StandardField.NUMBER, "2"); entry.setField(StandardField.PUBLISHER, "Brill"); - entry.setField(StandardField.KEYWORDS, "Political Science and International Relations, Linguistics and Language, Philosophy"); assertEquals(Optional.of(entry), fetcher.performSearchById("10.1023/a:1003473214310")); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java b/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java index 965fde89a1a..eeffcf5aa98 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/IEEETest.java @@ -16,18 +16,25 @@ import org.jabref.model.entry.types.StandardEntryType; import org.jabref.testutils.category.FetcherTest; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.Answers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @FetcherTest class IEEETest implements SearchBasedFetcherCapabilityTest, PagedSearchFetcherTest { - private final BibEntry IGOR_NEWCOMERS = new BibEntry(StandardEntryType.InProceedings) + private static ImportFormatPreferences importFormatPreferences; + + private static ImporterPreferences importerPreferences; + + private static final BibEntry IGOR_NEWCOMERS = new BibEntry(StandardEntryType.InProceedings) .withField(StandardField.AUTHOR, "Igor Steinmacher and Tayana Uchoa Conte and Christoph Treude and Marco AurĆ©lio Gerosa") .withField(StandardField.DATE, "14-22 May 2016") .withField(StandardField.YEAR, "2016") @@ -45,65 +52,76 @@ class IEEETest implements SearchBasedFetcherCapabilityTest, PagedSearchFetcherTe .withField(StandardField.FILE, ":https\\://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=7886910:PDF"); private IEEE fetcher; - private BibEntry entry; - @BeforeEach - void setUp() { - ImportFormatPreferences importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); + @BeforeAll + static void ensureIeeeIsAvailable() throws Exception { + importFormatPreferences = mock(ImportFormatPreferences.class, Answers.RETURNS_DEEP_STUBS); when(importFormatPreferences.bibEntryPreferences().getKeywordSeparator()).thenReturn(','); - ImporterPreferences importerPreferences = mock(ImporterPreferences.class); + importerPreferences = mock(ImporterPreferences.class); when(importerPreferences.getApiKeys()).thenReturn(FXCollections.emptyObservableSet()); + IEEE ieee = new IEEE(importFormatPreferences, importerPreferences); + + assumeFalse(List.of().equals(ieee.performSearch("article_number:8801912"))); + } + + @BeforeEach + void setUp() { fetcher = new IEEE(importFormatPreferences, importerPreferences); - entry = new BibEntry(); } @Test + @Disabled("IEEE seems to block us") void findByDOI() throws Exception { - entry.setField(StandardField.DOI, "10.1109/ACCESS.2016.2535486"); + BibEntry entry = new BibEntry().withField(StandardField.DOI, "10.1109/ACCESS.2016.2535486"); assertEquals(Optional.of(new URL("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @Test + @Disabled("IEEE seems to block us") void findByDocumentUrl() throws Exception { - entry.setField(StandardField.URL, "https://ieeexplore.ieee.org/document/7421926/"); + BibEntry entry = new BibEntry().withField(StandardField.URL, "https://ieeexplore.ieee.org/document/7421926/"); assertEquals(Optional.of(new URL("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @Test + @Disabled("IEEE seems to block us") void findByURL() throws Exception { - entry.setField(StandardField.URL, "https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=7421926&ref="); + BibEntry entry = new BibEntry().withField(StandardField.URL, "https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=7421926&ref="); assertEquals(Optional.of(new URL("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @Test + @Disabled("IEEE blocks us - works in browser") void findByOldURL() throws Exception { - entry.setField(StandardField.URL, "https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7421926"); + BibEntry entry = new BibEntry().withField(StandardField.URL, "https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7421926"); assertEquals(Optional.of(new URL("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @Test + @Disabled("IEEE seems to block us") void findByDOIButNotURL() throws Exception { - entry.setField(StandardField.DOI, "10.1109/ACCESS.2016.2535486"); - entry.setField(StandardField.URL, "http://dx.doi.org/10.1109/ACCESS.2016.2535486"); + BibEntry entry = new BibEntry() + .withField(StandardField.DOI, "10.1109/ACCESS.2016.2535486") + .withField(StandardField.URL, "http://dx.doi.org/10.1109/ACCESS.2016.2535486"); assertEquals(Optional.of(new URL("https://ieeexplore.ieee.org/ielx7/6287639/7419931/07421926.pdf?tp=&arnumber=7421926&isnumber=7419931&ref=")), fetcher.findFullText(entry)); } @Test void notFoundByURL() throws Exception { - entry.setField(StandardField.URL, "http://dx.doi.org/10.1109/ACCESS.2016.2535486"); + BibEntry entry = new BibEntry().withField(StandardField.URL, "http://dx.doi.org/10.1109/ACCESS.2016.2535486"); assertEquals(Optional.empty(), fetcher.findFullText(entry)); } @Test void notFoundByDOI() throws Exception { - entry.setField(StandardField.DOI, "10.1021/bk-2006-WWW.ch014"); + BibEntry entry = new BibEntry().withField(StandardField.DOI, "10.1021/bk-2006-WWW.ch014"); assertEquals(Optional.empty(), fetcher.findFullText(entry)); } @@ -124,8 +142,9 @@ void searchResultHasNoKeywordTerms() throws Exception { .withField(StandardField.VOLUME, "16") .withField(StandardField.KEYWORDS, "Batteries, Generators, Economics, Power quality, State of charge, Harmonic analysis, Control systems, Battery, diesel generator (DG), distributed generation, power quality, photovoltaic (PV), voltage source converter (VSC)"); - List fetchedEntries = fetcher.performSearch("article_number:8801912"); // article number - fetchedEntries.forEach(entry -> entry.clearField(StandardField.ABSTRACT)); // Remove abstract due to copyright); + List fetchedEntries = fetcher.performSearch("article_number:8801912"); + // Abstract should not be included in JabRef tests (copyrighted) + fetchedEntries.forEach(entry -> entry.clearField(StandardField.ABSTRACT)); assertEquals(Collections.singletonList(expected), fetchedEntries); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ISIDOREFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ISIDOREFetcherTest.java index 1b24ea179c9..6fd5239ce0c 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ISIDOREFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ISIDOREFetcherTest.java @@ -67,8 +67,8 @@ public void checkThesis() throws FetcherException { List actual = fetcher.performSearch("Mapping English L2 errors: an integrated system and textual approach"); - // Fetcher returns the same entry twice. - assertEquals(List.of(expected, expected), actual); + // Fetcher returns the same entry twice. Since 2024, it also returns an additional entry. We just ignore this for now. + assertEquals(expected, actual.getFirst()); } @Test diff --git a/src/test/java/org/jabref/logic/importer/fetcher/ScholarArchiveFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/ScholarArchiveFetcherTest.java index 0af2c4f0eb4..3ebe337344c 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/ScholarArchiveFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/ScholarArchiveFetcherTest.java @@ -9,6 +9,7 @@ import org.jabref.testutils.category.FetcherTest; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -22,16 +23,10 @@ public class ScholarArchiveFetcherTest { @BeforeEach public void setUp() { fetcher = new ScholarArchiveFetcher(); - bibEntry = new BibEntry(StandardEntryType.InCollection) - .withField(StandardField.TITLE, "Query expansion using associated queries") - .withField(StandardField.AUTHOR, "Billerbeck, Bodo and Scholer, Falk and Williams, Hugh E. and Zobel, Justin") - .withField(StandardField.VOLUME, "0") - .withField(StandardField.DOI, "10.1145/956863.956866") - .withField(StandardField.JOURNAL, "Proceedings of the twelfth international conference on Information and knowledge management - CIKM '03") - .withField(StandardField.PUBLISHER, "ACM Press") - .withField(StandardField.TYPE, "paper-conference") - .withField(StandardField.YEAR, "2003") - .withField(StandardField.URL, "https://web.archive.org/web/20170810164449/http://goanna.cs.rmit.edu.au/~jz/fulltext/cikm03.pdf"); + bibEntry = new BibEntry(StandardEntryType.InProceedings) + .withField(StandardField.TITLE, "BPELscript: A Simplified Script Syntax for WS-BPEL 2.0") + .withField(StandardField.AUTHOR, "Marc Bischof and Oliver Kopp and Tammo van Lessen and Frank Leymann ") + .withField(StandardField.YEAR, "2009"); } @Test @@ -40,8 +35,9 @@ public void getNameReturnsCorrectName() { } @Test + @Disabled("We seem to be blocked") public void performSearchReturnsExpectedResults() throws FetcherException { - List fetchedEntries = fetcher.performSearch("query"); + List fetchedEntries = fetcher.performSearch("bpelscript"); fetchedEntries.forEach(entry -> entry.clearField(StandardField.ABSTRACT)); assertTrue(fetchedEntries.contains(bibEntry), "Found the following entries " + fetchedEntries); } diff --git a/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java b/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java index 32045374354..04f53818564 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/SemanticScholarTest.java @@ -44,12 +44,10 @@ public class SemanticScholarTest implements PagedSearchFetcherTest { .withField(StandardField.VENUE, "International Conference on Software Engineering"); private SemanticScholar fetcher; - private BibEntry entry; @BeforeEach void setUp() { fetcher = new SemanticScholar(importerPreferences); - entry = new BibEntry(); } @Test @@ -64,7 +62,7 @@ void getDocument() throws IOException, FetcherException { @Disabled("Returns a DOI instead of the required link") @DisabledOnCIServer("CI server is unreliable") void fullTextFindByDOI() throws Exception { - entry.setField(StandardField.DOI, "10.1038/nrn3241"); + BibEntry entry = new BibEntry().withField(StandardField.DOI, "10.1038/nrn3241"); assertEquals( Optional.of(new URI("https://europepmc.org/articles/pmc4907333?pdf=render").toURL()), fetcher.findFullText(entry) @@ -73,6 +71,7 @@ void fullTextFindByDOI() throws Exception { @Test @DisabledOnCIServer("CI server is unreliable") + @Disabled("Sometimes, does not find any thing") void fullTextFindByDOIAlternate() throws Exception { assertEquals( Optional.of(new URI("https://pdfs.semanticscholar.org/7f6e/61c254bc2df38a784c1228f56c13317caded.pdf").toURL()), @@ -89,8 +88,8 @@ void fullTextSearchOnEmptyEntry() throws IOException, FetcherException { @Test @DisabledOnCIServer("CI server is unreliable") void fullTextNotFoundByDOI() throws IOException, FetcherException { - entry = new BibEntry().withField(StandardField.DOI, DOI); - entry.setField(StandardField.DOI, "10.1021/bk-2006-WWW.ch014"); + BibEntry entry = new BibEntry().withField(StandardField.DOI, DOI) + .withField(StandardField.DOI, "10.1021/bk-2006-WWW.ch014"); assertEquals(Optional.empty(), fetcher.findFullText(entry)); } @@ -98,7 +97,7 @@ void fullTextNotFoundByDOI() throws IOException, FetcherException { @Test @DisabledOnCIServer("CI server is unreliable") void fullTextFindByArXiv() throws Exception { - entry = new BibEntry().withField(StandardField.EPRINT, "1407.3561") + BibEntry entry = new BibEntry().withField(StandardField.EPRINT, "1407.3561") .withField(StandardField.ARCHIVEPREFIX, "arXiv"); assertEquals( Optional.of(new URI("https://arxiv.org/pdf/1407.3561.pdf").toURL()), @@ -130,6 +129,7 @@ void getURLForQueryWithLucene() throws QueryNodeParseException, MalformedURLExce } @Test + @Disabled("We seem to be blocked") void searchByQueryFindsEntry() throws Exception { BibEntry master = new BibEntry(StandardEntryType.Article) .withField(StandardField.AUTHOR, "Tobias Diez") @@ -145,6 +145,7 @@ void searchByQueryFindsEntry() throws Exception { } @Test + @Disabled("We seem to be blocked") void searchByPlainQueryFindsEntry() throws Exception { List fetchedEntries = fetcher.performSearch("Overcoming Open Source Project Entry Barriers with a Portal for Newcomers"); // Abstract should not be included in JabRef tests @@ -153,6 +154,7 @@ void searchByPlainQueryFindsEntry() throws Exception { } @Test + @Disabled("We seem to be blocked") void searchByQuotedQueryFindsEntry() throws Exception { List fetchedEntries = fetcher.performSearch("\"Overcoming Open Source Project Entry Barriers with a Portal for Newcomers\""); // Abstract should not be included in JabRef tests @@ -166,6 +168,7 @@ public void performSearchByEmptyQuery() throws Exception { } @Test + @Disabled("We seem to be blocked") public void findByEntry() throws Exception { BibEntry barrosEntry = new BibEntry(StandardEntryType.Article) .withField(StandardField.TITLE, "Formalising BPMN Service Interaction Patterns") @@ -175,7 +178,7 @@ public void findByEntry() throws Exception { .withField(StandardField.URL, "https://www.semanticscholar.org/paper/3bb026fd67db7d8e0e25de3189d6b7031b12783e") .withField(StandardField.VENUE, "The Practice of Enterprise Modeling"); - entry.withField(StandardField.TITLE, "Formalising BPMN Service Interaction Patterns"); + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "Formalising BPMN Service Interaction Patterns"); BibEntry actual = fetcher.performSearch(entry).getFirst(); // Abstract should not be included in JabRef tests actual.clearField(StandardField.ABSTRACT); diff --git a/src/test/java/org/jabref/logic/importer/fileformat/PdfGrobidImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/PdfGrobidImporterTest.java index 33977668ee3..5f9153434fa 100644 --- a/src/test/java/org/jabref/logic/importer/fileformat/PdfGrobidImporterTest.java +++ b/src/test/java/org/jabref/logic/importer/fileformat/PdfGrobidImporterTest.java @@ -14,6 +14,7 @@ import org.jabref.testutils.category.FetcherTest; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.Answers; @@ -47,10 +48,12 @@ public void getExtensions() { } @Test + @Disabled("Currently does not return anything") public void importEntries() throws URISyntaxException { Path file = Path.of(PdfGrobidImporterTest.class.getResource("LNCS-minimal.pdf").toURI()); List bibEntries = importer.importDatabase(file).getDatabase().getEntries(); + // TODO: Rewrite using our logic of full BibEntries assertEquals(1, bibEntries.size()); BibEntry be0 = bibEntries.getFirst(); From 4f877400c57953feefb4ea77adc14c35ede5613b Mon Sep 17 00:00:00 2001 From: Loay Ghreeb <52158423+LoayGhreeb@users.noreply.github.com> Date: Tue, 30 Apr 2024 09:05:56 +0300 Subject: [PATCH 24/54] Update search results when active database changes (#11268) Merged origin/update-search-results into update-search-results --- src/main/java/org/jabref/gui/search/GlobalSearchBar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java index de9319c93a3..5c18e02243a 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java @@ -238,7 +238,7 @@ public GlobalSearchBar(LibraryTabContainer tabContainer, query -> setSearchTerm(query.map(SearchQuery::getQuery).orElse(""))); this.searchQueryProperty.addListener((obs, oldValue, newValue) -> newValue.ifPresent(this::updateSearchResultsForQuery)); - this.searchQueryProperty.addListener((obs, oldValue, newValue) -> searchQueryProperty.get().ifPresent(this::updateSearchResultsForQuery)); + this.stateManager.activeDatabaseProperty().addListener(obs -> searchQueryProperty.get().ifPresent(this::updateSearchResultsForQuery)); /* * The listener tracks a change on the focus property value. * This happens, from active (user types a query) to inactive / focus From dc5b413fd875da5bc37d3a5bfccb2ac6819bcebc Mon Sep 17 00:00:00 2001 From: HoussemNasri Date: Tue, 30 Apr 2024 12:31:58 +0100 Subject: [PATCH 25/54] Fix drag and dropping entries not always working - It doesn't work if the destination library has no group structure; "All entries" is the only group. --- CHANGELOG.md | 1 + .../org/jabref/gui/frame/FrameDndHandler.java | 75 ++++++++++++------- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99fc9dfa8fa..c1e90236059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed crash on opening the entry editor when auto completion is enabled. [#11188](https://github.com/JabRef/jabref/issues/11188) - We fixed the usage of the key binding for "Clear search" (default: Escape). [#10764](https://github.com/JabRef/jabref/issues/10764) - We fixed an issue where library shown as unsaved and marked (*) after accepting changes made externally to the file. [#11027](https://github.com/JabRef/jabref/issues/11027) +- We fixed an issue where drag and dropping entries from one library to another was not always working. [#11254](https://github.com/JabRef/jabref/issues/11254) ### Removed diff --git a/src/main/java/org/jabref/gui/frame/FrameDndHandler.java b/src/main/java/org/jabref/gui/frame/FrameDndHandler.java index 75fd0aebb77..a065870cbe5 100644 --- a/src/main/java/org/jabref/gui/frame/FrameDndHandler.java +++ b/src/main/java/org/jabref/gui/frame/FrameDndHandler.java @@ -26,8 +26,12 @@ import org.jabref.model.groups.GroupTreeNode; import com.tobiasdiez.easybind.EasyBind; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FrameDndHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(FrameDndHandler.class); + private final TabPane tabPane; private final Supplier scene; private final Supplier openDatabaseAction; @@ -81,45 +85,62 @@ private void onTabDragDropped(Node destinationTabNode, DragEvent tabDragEvent, T tabDragEvent.setDropCompleted(true); tabDragEvent.consume(); } else { - if (stateManager.getActiveDatabase().isEmpty() - || stateManager.getActiveDatabase().get().getMetaData().getGroups().isEmpty()) { + if (stateManager.getActiveDatabase().isEmpty()) { + LOGGER.warn("Active library is empty when dropping entries"); return; } + LibraryTab destinationLibraryTab = null; for (Tab libraryTab : tabPane.getTabs()) { if (libraryTab.getId().equals(destinationTabNode.getId()) && !tabPane.getSelectionModel().getSelectedItem().equals(libraryTab)) { - LibraryTab destinationLibraryTab = (LibraryTab) libraryTab; - if (hasGroups(dragboard)) { - List groupPathToSources = getGroups(dragboard); - - copyRootNode(destinationLibraryTab); - - GroupTreeNode destinationLibraryGroupRoot = destinationLibraryTab - .getBibDatabaseContext() - .getMetaData() - .getGroups().get(); - - GroupTreeNode groupsTreeNode = stateManager.getActiveDatabase().get() - .getMetaData() - .getGroups() - .get(); - - for (String pathToSource : groupPathToSources) { - GroupTreeNode groupTreeNodeToCopy = groupsTreeNode - .getChildByPath(pathToSource) - .get(); - copyGroupTreeNode((LibraryTab) libraryTab, destinationLibraryGroupRoot, groupTreeNodeToCopy); - } - return; - } - destinationLibraryTab.dropEntry(stateManager.getLocalDragboard().getBibEntries()); + destinationLibraryTab = (LibraryTab) libraryTab; + break; } } + + if (destinationLibraryTab == null) { + LOGGER.warn("Failed to find library tab to drop into"); + return; + } + + if (hasEntries(dragboard)) { + destinationLibraryTab.dropEntry(stateManager.getLocalDragboard().getBibEntries()); + } else if (hasGroups(dragboard)) { + dropGroups(dragboard, destinationLibraryTab); + } + tabDragEvent.consume(); } } + private void dropGroups(Dragboard dragboard, LibraryTab destinationLibraryTab) { + List groupPathToSources = getGroups(dragboard); + + copyRootNode(destinationLibraryTab); + + GroupTreeNode destinationLibraryGroupRoot = destinationLibraryTab + .getBibDatabaseContext() + .getMetaData() + .getGroups().get(); + + GroupTreeNode groupsTreeNode = stateManager.getActiveDatabase().get() + .getMetaData() + .getGroups() + .get(); + + for (String pathToSource : groupPathToSources) { + GroupTreeNode groupTreeNodeToCopy = groupsTreeNode + .getChildByPath(pathToSource) + .get(); + copyGroupTreeNode(destinationLibraryTab, destinationLibraryGroupRoot, groupTreeNodeToCopy); + } + } + + private boolean hasEntries(Dragboard dragboard) { + return dragboard.hasContent(DragAndDropDataFormats.ENTRIES); + } + private void onTabDragOver(DragEvent event, DragEvent tabDragEvent, Tab dndIndicator) { if (hasBibFiles(tabDragEvent.getDragboard()) || hasGroups(tabDragEvent.getDragboard())) { tabDragEvent.acceptTransferModes(TransferMode.ANY); From 9f259c9c22e570ab33b2f45d608add1e064b1391 Mon Sep 17 00:00:00 2001 From: HoussemNasri Date: Tue, 30 Apr 2024 12:34:34 +0100 Subject: [PATCH 26/54] Fix drag and dropping entries create a shallow copy --- CHANGELOG.md | 1 + src/main/java/org/jabref/gui/frame/FrameDndHandler.java | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e90236059..4e3a4c875e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed the usage of the key binding for "Clear search" (default: Escape). [#10764](https://github.com/JabRef/jabref/issues/10764) - We fixed an issue where library shown as unsaved and marked (*) after accepting changes made externally to the file. [#11027](https://github.com/JabRef/jabref/issues/11027) - We fixed an issue where drag and dropping entries from one library to another was not always working. [#11254](https://github.com/JabRef/jabref/issues/11254) +- We fixed an issue where drag and dropping entries created a shallow copy. [#11160](https://github.com/JabRef/jabref/issues/11160) ### Removed diff --git a/src/main/java/org/jabref/gui/frame/FrameDndHandler.java b/src/main/java/org/jabref/gui/frame/FrameDndHandler.java index a065870cbe5..424b3512488 100644 --- a/src/main/java/org/jabref/gui/frame/FrameDndHandler.java +++ b/src/main/java/org/jabref/gui/frame/FrameDndHandler.java @@ -105,7 +105,10 @@ private void onTabDragDropped(Node destinationTabNode, DragEvent tabDragEvent, T } if (hasEntries(dragboard)) { - destinationLibraryTab.dropEntry(stateManager.getLocalDragboard().getBibEntries()); + List entryCopies = stateManager.getLocalDragboard().getBibEntries() + .stream().map(entry -> (BibEntry) entry.clone()) + .toList(); + destinationLibraryTab.dropEntry(entryCopies); } else if (hasGroups(dragboard)) { dropGroups(dragboard, destinationLibraryTab); } From ca16e86dd8bb16b7051bd1f0b8ff615acdf36524 Mon Sep 17 00:00:00 2001 From: Christoph Date: Thu, 2 May 2024 11:31:23 +0200 Subject: [PATCH 27/54] Fix import selected groups (#11271) * Fix import entries to custom groups only working for one entry * changelog * rename getSelectedGroups, prefix uid add changelog entries --- CHANGELOG.md | 2 ++ src/main/java/org/jabref/gui/LibraryTab.java | 4 +++- .../java/org/jabref/gui/StateManager.java | 14 +++++------ .../gui/externalfiles/ImportHandler.java | 2 +- .../jabref/gui/groups/GroupNodeViewModel.java | 2 +- .../jabref/gui/groups/GroupTreeViewModel.java | 4 ++-- .../model/database/BibDatabaseContext.java | 24 +++++++++++++++---- .../gui/groups/GroupTreeViewModelTest.java | 2 +- 8 files changed, 37 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9bb0871b9..410b5341a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where library shown as unsaved and marked (*) after accepting changes made externally to the file. [#11027](https://github.com/JabRef/jabref/issues/11027) - We fixed an issue where drag and dropping entries from one library to another was not always working. [#11254](https://github.com/JabRef/jabref/issues/11254) - We fixed an issue where drag and dropping entries created a shallow copy. [#11160](https://github.com/JabRef/jabref/issues/11160) +- We fixed an issue where imports to a custom group would only work for the first entry [#11085](https://github.com/JabRef/jabref/issues/11085), [#11269](https://github.com/JabRef/jabref/issues/11269) +- We fixed an issue where a new entry was not added to the selected group [#8933](https://github.com/JabRef/jabref/issues/8933) ### Removed diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 02ee691c747..ff2c9e4ba20 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -857,6 +857,8 @@ private void onClosed(Event event) { } catch (RuntimeException e) { LOGGER.error("Problem when shutting down backup manager", e); } + // clean up the groups map + stateManager.clearSelectedGroups(bibDatabaseContext); } /** @@ -1033,7 +1035,7 @@ public void listen(EntriesAddedEvent addedEntriesEvent) { // Automatically add new entries to the selected group (or set of groups) if (preferencesService.getGroupsPreferences().shouldAutoAssignGroup()) { - stateManager.getSelectedGroup(bibDatabaseContext).forEach( + stateManager.getSelectedGroups(bibDatabaseContext).forEach( selectedGroup -> selectedGroup.addEntriesToGroup(addedEntriesEvent.getBibEntries())); } } diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java index 010375fc1c5..9513c3fe3f1 100644 --- a/src/main/java/org/jabref/gui/StateManager.java +++ b/src/main/java/org/jabref/gui/StateManager.java @@ -57,13 +57,13 @@ public class StateManager { private final OptionalObjectProperty activeTab = OptionalObjectProperty.empty(); private final ReadOnlyListWrapper activeGroups = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); private final ObservableList selectedEntries = FXCollections.observableArrayList(); - private final ObservableMap> selectedGroups = FXCollections.observableHashMap(); + private final ObservableMap> selectedGroups = FXCollections.observableHashMap(); private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty(); private final OptionalObjectProperty activeGlobalSearchQuery = OptionalObjectProperty.empty(); private final IntegerProperty globalSearchResultSize = new SimpleIntegerProperty(0); private final ObservableMap searchResultMap = FXCollections.observableHashMap(); private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty(); - private final ObservableList, Task>> backgroundTasks = FXCollections.observableArrayList(task -> new Observable[]{task.getValue().progressProperty(), task.getValue().runningProperty()}); + private final ObservableList, Task>> backgroundTasks = FXCollections.observableArrayList(task -> new Observable[] {task.getValue().progressProperty(), task.getValue().runningProperty()}); private final EasyBinding anyTaskRunning = EasyBind.reduce(backgroundTasks, tasks -> tasks.map(Pair::getValue).anyMatch(Task::isRunning)); private final EasyBinding anyTasksThatWillNotBeRecoveredRunning = EasyBind.reduce(backgroundTasks, tasks -> tasks.anyMatch(task -> !task.getKey().willBeRecoveredAutomatically() && task.getValue().isRunning())); private final EasyBinding tasksProgress = EasyBind.reduce(backgroundTasks, tasks -> tasks.map(Pair::getValue).filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1)); @@ -75,7 +75,7 @@ public class StateManager { private final ObservableList searchHistory = FXCollections.observableArrayList(); public StateManager() { - activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElseOpt(null))); + activeGroups.bind(Bindings.valueAt(selectedGroups, activeDatabase.orElseOpt(null).map(BibDatabaseContext::getUid))); } public ObservableList getVisibleSidePaneComponents() { @@ -140,16 +140,16 @@ public void setSelectedEntries(List newSelectedEntries) { public void setSelectedGroups(BibDatabaseContext database, List newSelectedGroups) { Objects.requireNonNull(newSelectedGroups); - selectedGroups.put(database, FXCollections.observableArrayList(newSelectedGroups)); + selectedGroups.put(database.getUid(), FXCollections.observableArrayList(newSelectedGroups)); } - public ObservableList getSelectedGroup(BibDatabaseContext database) { - ObservableList selectedGroupsForDatabase = selectedGroups.get(database); + public ObservableList getSelectedGroups(BibDatabaseContext context) { + ObservableList selectedGroupsForDatabase = selectedGroups.get(context.getUid()); return selectedGroupsForDatabase != null ? selectedGroupsForDatabase : FXCollections.observableArrayList(); } public void clearSelectedGroups(BibDatabaseContext database) { - selectedGroups.remove(database); + selectedGroups.remove(database.getUid()); } public Optional getActiveDatabase() { diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index 8465c4305f6..96ad7e46e4d 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -194,7 +194,7 @@ public void importCleanedEntries(List entries) { bibDatabaseContext.getDatabase().insertEntries(entries); generateKeys(entries); setAutomaticFields(entries); - addToGroups(entries, stateManager.getSelectedGroup(bibDatabaseContext)); + addToGroups(entries, stateManager.getSelectedGroups(bibDatabaseContext)); } public void importEntryWithDuplicateCheck(BibDatabaseContext bibDatabaseContext, BibEntry entry) { diff --git a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java index bd113bf249e..dccd1fef498 100644 --- a/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java @@ -262,7 +262,7 @@ private void refreshGroup() { DefaultTaskExecutor.runInJavaFXThread(() -> { updateMatchedEntries(); // Update the entries matched by the group // "Re-add" to the selected groups if it were selected, this refreshes the entries the user views - ObservableList selectedGroups = this.stateManager.getSelectedGroup(this.databaseContext); + ObservableList selectedGroups = this.stateManager.getSelectedGroups(this.databaseContext); if (selectedGroups.remove(this.groupNode)) { selectedGroups.add(this.groupNode); } diff --git a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java index 4efab42033b..de0c579699b 100644 --- a/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java +++ b/src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java @@ -153,11 +153,11 @@ private void onActiveDatabaseChanged(Optional newDatabase) { .orElse(GroupNodeViewModel.getAllEntriesGroup(newDatabase.get(), stateManager, taskExecutor, localDragboard, preferences)); rootGroup.setValue(newRoot); - if (stateManager.getSelectedGroup(newDatabase.get()).isEmpty()) { + if (stateManager.getSelectedGroups(newDatabase.get()).isEmpty()) { stateManager.setSelectedGroups(newDatabase.get(), Collections.singletonList(newRoot.getGroupNode())); } selectedGroups.setAll( - stateManager.getSelectedGroup(newDatabase.get()).stream() + stateManager.getSelectedGroups(newDatabase.get()).stream() .map(selectedGroup -> new GroupNodeViewModel(newDatabase.get(), stateManager, taskExecutor, selectedGroup, localDragboard, preferences)) .collect(Collectors.toList())); } else { diff --git a/src/main/java/org/jabref/model/database/BibDatabaseContext.java b/src/main/java/org/jabref/model/database/BibDatabaseContext.java index b43f7069a24..f8d4c3dfdcf 100644 --- a/src/main/java/org/jabref/model/database/BibDatabaseContext.java +++ b/src/main/java/org/jabref/model/database/BibDatabaseContext.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; import org.jabref.architecture.AllowedToUseLogic; @@ -33,7 +34,7 @@ * and the options relevant for this file in Defaults. *

*

- * To get an instance for a .bib file, use {@link org.jabref.logic.importer.fileformat.BibtexParser}. + * To get an instance for a .bib file, use {@link org.jabref.logic.importer.fileformat.BibtexParser}. *

*/ @AllowedToUseLogic("because it needs access to shared database features") @@ -44,6 +45,12 @@ public class BibDatabaseContext { private final BibDatabase database; private MetaData metaData; + /** + * Generate a random UID for unique of the concrete context + * In contrast to hashCode this stays unique + */ + private final String uid = "bibdatabasecontext_" + UUID.randomUUID(); + /** * The path where this database was last saved to. */ @@ -127,9 +134,9 @@ public boolean isBiblatexMode() { */ public boolean isStudy() { return this.getDatabasePath() - .map(path -> path.getFileName().toString().equals(Crawler.FILENAME_STUDY_RESULT_BIB) && - Files.exists(path.resolveSibling(StudyRepository.STUDY_DEFINITION_FILE_NAME))) - .orElse(false); + .map(path -> path.getFileName().toString().equals(Crawler.FILENAME_STUDY_RESULT_BIB) && + Files.exists(path.resolveSibling(StudyRepository.STUDY_DEFINITION_FILE_NAME))) + .orElse(false); } /** @@ -286,4 +293,13 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(database, metaData, path, location); } + + /** + * Get the generated UID for the current context. Can be used to distinguish contexts with changing metadata etc + * + * @return The generated UID in UUIDv4 format with the prefix bibdatabasecontext_ + */ + public String getUid() { + return uid; + } } diff --git a/src/test/java/org/jabref/gui/groups/GroupTreeViewModelTest.java b/src/test/java/org/jabref/gui/groups/GroupTreeViewModelTest.java index 5f7f2369134..bdb27ee2fc1 100644 --- a/src/test/java/org/jabref/gui/groups/GroupTreeViewModelTest.java +++ b/src/test/java/org/jabref/gui/groups/GroupTreeViewModelTest.java @@ -61,7 +61,7 @@ void rootGroupIsAllEntriesByDefault() { @Test void rootGroupIsSelectedByDefault() { - assertEquals(groupTree.rootGroupProperty().get().getGroupNode(), stateManager.getSelectedGroup(databaseContext).getFirst()); + assertEquals(groupTree.rootGroupProperty().get().getGroupNode(), stateManager.getSelectedGroups(databaseContext).getFirst()); } @Test From 43e77cbec268e33633db184e2e68e6a7921b30c6 Mon Sep 17 00:00:00 2001 From: compf Date: Fri, 3 May 2024 09:35:40 +0200 Subject: [PATCH 28/54] Refactored data clumps with the help of LLMs (research project) (#11181) * refactored a data clump * made DocumentAnnotation a record and moved comments * fixed checkstyle errors * fixed more checkstyle violatopns * removed fully qualified name * fixed documentation and naming issues --------- Co-authored-by: tschoemaker <=> --- .../backend/NamedRangeReferenceMark.java | 5 +++-- .../frontend/UpdateBibliography.java | 7 +++++-- .../model/openoffice/DocumentAnnotation.java | 15 +++++++++++++++ .../model/openoffice/uno/UnoBookmark.java | 9 ++++----- .../jabref/model/openoffice/uno/UnoNamed.java | 19 ++++++------------- .../openoffice/uno/UnoReferenceMark.java | 10 +++++----- .../model/openoffice/uno/UnoTextSection.java | 12 +++++------- 7 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 src/main/java/org/jabref/model/openoffice/DocumentAnnotation.java diff --git a/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java b/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java index 851b084b866..6683a67637a 100644 --- a/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java +++ b/src/main/java/org/jabref/logic/openoffice/backend/NamedRangeReferenceMark.java @@ -2,6 +2,7 @@ import java.util.Optional; +import org.jabref.model.openoffice.DocumentAnnotation; import org.jabref.model.openoffice.backend.NamedRange; import org.jabref.model.openoffice.uno.CreationException; import org.jabref.model.openoffice.uno.NoDocumentException; @@ -101,8 +102,8 @@ private static void createReprInDocument(XTextDocument doc, : left + right; cursor.getText().insertString(cursor, bracketedContent, true); - - UnoReferenceMark.create(doc, refMarkName, cursor, true /* absorb */); + DocumentAnnotation documentAnnotation = new DocumentAnnotation(doc, refMarkName, cursor, true /* absorb */); + UnoReferenceMark.create(documentAnnotation); // eat the first inserted space cursorBefore.goRight((short) 1, true); diff --git a/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java b/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java index 99e71e13725..47e63414c78 100644 --- a/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java +++ b/src/main/java/org/jabref/logic/openoffice/frontend/UpdateBibliography.java @@ -4,6 +4,7 @@ import org.jabref.logic.openoffice.style.OOBibStyle; import org.jabref.logic.openoffice.style.OOFormatBibliography; +import org.jabref.model.openoffice.DocumentAnnotation; import org.jabref.model.openoffice.ootext.OOText; import org.jabref.model.openoffice.ootext.OOTextIntoOO; import org.jabref.model.openoffice.style.CitedKeys; @@ -67,7 +68,8 @@ private static void createBibTextSection2(XTextDocument doc) // Alternatively, we could receive a cursor. XTextCursor textCursor = doc.getText().createTextCursor(); textCursor.gotoEnd(false); - UnoTextSection.create(doc, BIB_SECTION_NAME, textCursor, false); + DocumentAnnotation annotation = new DocumentAnnotation(doc, BIB_SECTION_NAME, textCursor, false); + UnoTextSection.create(annotation); } /** @@ -129,7 +131,8 @@ private static void populateBibTextSection(XTextDocument doc, initialParagraph.setString(""); UnoBookmark.removeIfExists(doc, BIB_SECTION_END_NAME); - UnoBookmark.create(doc, BIB_SECTION_END_NAME, cursor, true); + DocumentAnnotation documentAnnotation = new DocumentAnnotation(doc, BIB_SECTION_END_NAME, cursor, true); + UnoBookmark.create(documentAnnotation); cursor.collapseToEnd(); } diff --git a/src/main/java/org/jabref/model/openoffice/DocumentAnnotation.java b/src/main/java/org/jabref/model/openoffice/DocumentAnnotation.java new file mode 100644 index 00000000000..2d7037f7199 --- /dev/null +++ b/src/main/java/org/jabref/model/openoffice/DocumentAnnotation.java @@ -0,0 +1,15 @@ +package org.jabref.model.openoffice; + +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XTextRange; + +/** + * Represents a document annotation. + * + * @param doc The document + * @param name For the ReferenceMark, Bookmark, TextSection. If the name is already in use, LibreOffice may change the name. + * @param range Cursor marking the location or range for the thing to be inserted. + * @param absorb ReferenceMark, Bookmark and TextSection can incorporate a text range. If absorb is true, the text in the range becomes part of the thing. If absorb is false, the thing is inserted at the end of the range. + */ +public record DocumentAnnotation(XTextDocument doc, String name, XTextRange range, boolean absorb) { +} diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java b/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java index 5f4f5ea40d7..0f87ddcd21b 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoBookmark.java @@ -2,6 +2,8 @@ import java.util.Optional; +import org.jabref.model.openoffice.DocumentAnnotation; + import com.sun.star.container.NoSuchElementException; import com.sun.star.container.XNameAccess; import com.sun.star.container.XNamed; @@ -52,16 +54,13 @@ public static Optional getAnchor(XTextDocument doc, String name) *

* In LibreOffice the another name is in "{name}{number}" format. * - * @param name For the bookmark. - * @param range Cursor marking the location or range for the bookmark. - * @param absorb Shall we incorporate range? * @return The XNamed interface of the bookmark. * result.getName() should be checked by the caller, because its name may differ from the one requested. */ - public static XNamed create(XTextDocument doc, String name, XTextRange range, boolean absorb) + public static XNamed create(DocumentAnnotation documentAnnotation) throws CreationException { - return UnoNamed.insertNamedTextContent(doc, "com.sun.star.text.Bookmark", name, range, absorb); + return UnoNamed.insertNamedTextContent("com.sun.star.text.Bookmark", documentAnnotation); } /** diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java b/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java index d3bf46668d1..3352150ba85 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoNamed.java @@ -1,10 +1,10 @@ package org.jabref.model.openoffice.uno; +import org.jabref.model.openoffice.DocumentAnnotation; + import com.sun.star.container.XNamed; import com.sun.star.lang.XMultiServiceFactory; import com.sun.star.text.XTextContent; -import com.sun.star.text.XTextDocument; -import com.sun.star.text.XTextRange; public class UnoNamed { @@ -17,20 +17,13 @@ private UnoNamed() { * @param service For example "com.sun.star.text.ReferenceMark", "com.sun.star.text.Bookmark" or "com.sun.star.text.TextSection". *

* Passed to this.asXMultiServiceFactory().createInstance(service) The result is expected to support the XNamed and XTextContent interfaces. - * @param name For the ReferenceMark, Bookmark, TextSection. If the name is already in use, LibreOffice may change the name. - * @param range Marks the location or range for the thing to be inserted. - * @param absorb ReferenceMark, Bookmark and TextSection can incorporate a text range. If absorb is true, the text in the range becomes part of the thing. If absorb is false, the thing is inserted at the end of the range. * @return The XNamed interface, in case we need to check the actual name. */ - static XNamed insertNamedTextContent(XTextDocument doc, - String service, - String name, - XTextRange range, - boolean absorb) + static XNamed insertNamedTextContent(String service, DocumentAnnotation documentAnnotation) throws CreationException { - XMultiServiceFactory msf = UnoCast.cast(XMultiServiceFactory.class, doc).get(); + XMultiServiceFactory msf = UnoCast.cast(XMultiServiceFactory.class, documentAnnotation.doc()).get(); Object xObject; try { @@ -41,12 +34,12 @@ static XNamed insertNamedTextContent(XTextDocument doc, XNamed xNamed = UnoCast.cast(XNamed.class, xObject) .orElseThrow(() -> new IllegalArgumentException("Service is not an XNamed")); - xNamed.setName(name); + xNamed.setName(documentAnnotation.name()); // get XTextContent interface XTextContent xTextContent = UnoCast.cast(XTextContent.class, xObject) .orElseThrow(() -> new IllegalArgumentException("Service is not an XTextContent")); - range.getText().insertTextContent(range, xTextContent, absorb); + documentAnnotation.range().getText().insertTextContent(documentAnnotation.range(), xTextContent, documentAnnotation.absorb()); return xNamed; } } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java b/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java index 12e36fae46a..0b9f2adc6c0 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoReferenceMark.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Optional; +import org.jabref.model.openoffice.DocumentAnnotation; + import com.sun.star.container.NoSuchElementException; import com.sun.star.container.XNameAccess; import com.sun.star.container.XNamed; @@ -105,16 +107,14 @@ public static Optional getAnchor(XTextDocument doc, String name) /** * Insert a new reference mark at the provided cursor position. *

- * If {@code absorb} is true, the text in the cursor range will become the text with gray background. + * If {@code documentAnnotation.getAbsorb} is true, the text in the cursor range will become the text with gray background. *

* Note: LibreOffice 6.4.6.2 will create multiple reference marks with the same name without error or renaming. Its GUI does not allow this, but we can create them programmatically. In the GUI, clicking on any of those identical names will move the cursor to the same mark. * - * @param name For the reference mark. - * @param range Cursor marking the location or range for the reference mark. */ - public static XNamed create(XTextDocument doc, String name, XTextRange range, boolean absorb) + public static XNamed create(DocumentAnnotation documentAnnotation) throws CreationException { - return UnoNamed.insertNamedTextContent(doc, "com.sun.star.text.ReferenceMark", name, range, absorb); + return UnoNamed.insertNamedTextContent("com.sun.star.text.ReferenceMark", documentAnnotation); } } diff --git a/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java b/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java index 1717795cfa7..9e0c3fddf2d 100644 --- a/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java +++ b/src/main/java/org/jabref/model/openoffice/uno/UnoTextSection.java @@ -2,6 +2,8 @@ import java.util.Optional; +import org.jabref.model.openoffice.DocumentAnnotation; + import com.sun.star.container.NoSuchElementException; import com.sun.star.container.XNameAccess; import com.sun.star.container.XNamed; @@ -63,17 +65,13 @@ public static Optional getAnchor(XTextDocument doc, String name) /** * Create a text section with the provided name and insert it at the provided cursor. - * - * @param name The desired name for the section. - * @param range The location to insert at. - *

- * If an XTextSection by that name already exists, LibreOffice (6.4.6.2) creates a section with a name different from what we requested, in "Section {number}" format. + * If an XTextSection by that name already exists, LibreOffice (6.4.6.2) creates a section with a name different from what we requested, in "Section {number}" format */ - public static XNamed create(XTextDocument doc, String name, XTextRange range, boolean absorb) + public static XNamed create(DocumentAnnotation documentAnnotation) throws CreationException { - return UnoNamed.insertNamedTextContent(doc, "com.sun.star.text.TextSection", name, range, absorb); + return UnoNamed.insertNamedTextContent("com.sun.star.text.TextSection", documentAnnotation); } } From b4c4d4f1872fd8e695e52d945dedfa2f540df64a Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 3 May 2024 20:14:00 +0200 Subject: [PATCH 29/54] Update codesign action due to Node 16 deprecation (#11266) * Update deployment-arm64.yml * Update deployment-jdk-ea.yml * Update deployment.yml * Use hash instead of version --------- Co-authored-by: Oliver Kopp --- .github/workflows/deployment-arm64.yml | 4 ++-- .github/workflows/deployment-jdk-ea.yml | 4 ++-- .github/workflows/deployment.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index 69c4ac284a5..01675246e2b 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -82,14 +82,14 @@ jobs: security delete-keychain signing_temp.keychain ${{runner.temp}}/keychain/notarization.keychain || true - name: Setup OSX key chain on macOS-arm if: (steps.checksecrets.outputs.secretspresent == 'YES') - uses: apple-actions/import-codesign-certs@v2 + uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }} p12-password: ${{ secrets.OSX_CERT_PWD }} keychain-password: jabref - name: Setup OSX key chain on OSX for app id cert if: (steps.checksecrets.outputs.secretspresent == 'YES') - uses: apple-actions/import-codesign-certs@v2 + uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT_APPLICATION }} p12-password: ${{ secrets.OSX_CERT_PWD }} diff --git a/.github/workflows/deployment-jdk-ea.yml b/.github/workflows/deployment-jdk-ea.yml index 340a566df64..63d8e2a1dd5 100644 --- a/.github/workflows/deployment-jdk-ea.yml +++ b/.github/workflows/deployment-jdk-ea.yml @@ -200,14 +200,14 @@ jobs: run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir - name: Set up macOS key chain if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') - uses: apple-actions/import-codesign-certs@v2 + uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }} p12-password: ${{ secrets.OSX_CERT_PWD }} keychain-password: jabref - name: Set up macOS key chain for app id cert if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') - uses: apple-actions/import-codesign-certs@v2 + uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT_APPLICATION }} p12-password: ${{ secrets.OSX_CERT_PWD }} diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index c61282d61c0..0fe694d2a30 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -100,14 +100,14 @@ jobs: run: ./gradlew -i -PprojVersion="${{ steps.gitversion.outputs.AssemblySemVer }}" -PprojVersionInfo="${{ steps.gitversion.outputs.InformationalVersion }}" prepareModulesDir - name: Setup macOS key chain if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') - uses: apple-actions/import-codesign-certs@v2 + uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT }} p12-password: ${{ secrets.OSX_CERT_PWD }} keychain-password: jabref - name: Setup macOS key chain for app id cert if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') - uses: apple-actions/import-codesign-certs@v2 + uses: slidoapp/import-codesign-certs@1923310662e8682dd05b76b612b53301f431cd5d with: p12-file-base64: ${{ secrets.OSX_SIGNING_CERT_APPLICATION }} p12-password: ${{ secrets.OSX_CERT_PWD }} From 09683ed7df4bc03bfd71a4baddc46f77bcb5abb7 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 3 May 2024 20:17:08 +0200 Subject: [PATCH 30/54] Remove obsolete methods (#11272) * Remove obsolete method * Remove obsolete CLI parameter "console" * remove l10n --------- Co-authored-by: Siedlerchr --- src/main/java/org/jabref/cli/JabRefCLI.java | 24 ------------------- src/main/resources/l10n/JabRef_en.properties | 1 - .../java/org/jabref/cli/JabRefCLITest.java | 13 ---------- 3 files changed, 38 deletions(-) diff --git a/src/main/java/org/jabref/cli/JabRefCLI.java b/src/main/java/org/jabref/cli/JabRefCLI.java index 3d613468af5..cf78f1a2da2 100644 --- a/src/main/java/org/jabref/cli/JabRefCLI.java +++ b/src/main/java/org/jabref/cli/JabRefCLI.java @@ -189,9 +189,6 @@ private static Options getOptions() { options.addOption("v", "version", false, Localization.lang("Display version")); options.addOption(null, "debug", false, Localization.lang("Show debug level messages")); - // The "-console" option is handled by the install4j launcher - options.addOption(null, "console", false, Localization.lang("Show console output (only when the launcher is used)")); - options.addOption(Option .builder("i") .longOpt("import") @@ -372,25 +369,4 @@ protected static String alignStringTable(List> table) { return sb.toString(); } - - /** - * Creates and wraps a multi-line and colon-seperated string from a List of Strings. - */ - protected static String wrapStringList(List list, int firstLineIndentation) { - StringBuilder builder = new StringBuilder(); - int lastBreak = -firstLineIndentation; - - for (String line : list) { - if (((builder.length() + 2 + line.length()) - lastBreak) > WIDTH) { - builder.append(",\n"); - lastBreak = builder.length(); - builder.append(WRAPPED_LINE_PREFIX); - } else if (builder.length() > 0) { - builder.append(", "); - } - builder.append(line); - } - - return builder.toString(); - } } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index fdb5076f8c5..b8326bbbe12 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1880,7 +1880,6 @@ Could\ not\ copy\ file=Could not copy file Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=Copied %0 files of %1 successfully to %2 Rename\ failed=Rename failed JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef cannot access the file because it is being used by another process. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Show console output (only when the launcher is used) Remove\ line\ breaks=Remove line breaks Removes\ all\ line\ breaks\ in\ the\ field\ content.=Removes all line breaks in the field content. diff --git a/src/test/java/org/jabref/cli/JabRefCLITest.java b/src/test/java/org/jabref/cli/JabRefCLITest.java index dfcd8478cd7..8fcbbd01013 100644 --- a/src/test/java/org/jabref/cli/JabRefCLITest.java +++ b/src/test/java/org/jabref/cli/JabRefCLITest.java @@ -133,19 +133,6 @@ void successfulParsingOfBibtexImportLong() throws Exception { assertEquals(bibtex, cli.getBibtexImport()); } - @Test - void wrapStringList() { - List given = List.of("html", "simplehtml", "docbook5", "docbook4", "din1505", "bibordf", "tablerefs", "listrefs", - "tablerefsabsbib", "harvard", "iso690rtf", "iso690txt", "endnote", "oocsv", "ris", "misq", "yaml", "bibtexml", "oocalc", "ods", - "MSBib", "mods", "xmp", "pdf", "bib"); - String expected = """ - Available export formats: html, simplehtml, docbook5, docbook4, din1505, bibordf, tablerefs, - listrefs, tablerefsabsbib, harvard, iso690rtf, iso690txt, endnote, oocsv, ris, misq, yaml, bibtexml, - oocalc, ods, MSBib, mods, xmp, pdf, bib"""; - - assertEquals(expected, "Available export formats: " + JabRefCLI.wrapStringList(given, 26)); - } - @Test void alignStringTable() { List> given = List.of( From 505ea81daa5a033c100f2aa2fed29a145a202333 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 6 May 2024 08:21:15 +0200 Subject: [PATCH 31/54] New Crowdin updates (#11274) * New translations jabref_en.properties (French) * New translations jabref_en.properties (Spanish) * New translations jabref_en.properties (German) * New translations jabref_en.properties (Greek) * New translations jabref_en.properties (Italian) * New translations jabref_en.properties (Japanese) * New translations jabref_en.properties (Korean) * New translations jabref_en.properties (Dutch) * New translations jabref_en.properties (Polish) * New translations jabref_en.properties (Portuguese) * New translations jabref_en.properties (Russian) * New translations jabref_en.properties (Swedish) * New translations jabref_en.properties (Turkish) * New translations jabref_en.properties (Ukrainian) * New translations jabref_en.properties (Chinese Simplified) * New translations jabref_en.properties (Chinese Traditional) * New translations jabref_en.properties (Vietnamese) * New translations jabref_en.properties (Portuguese, Brazilian) --- src/main/resources/l10n/JabRef_de.properties | 9 ++--- src/main/resources/l10n/JabRef_el.properties | 2 -- src/main/resources/l10n/JabRef_es.properties | 5 --- src/main/resources/l10n/JabRef_fr.properties | 16 ++++++--- src/main/resources/l10n/JabRef_it.properties | 16 ++++++--- src/main/resources/l10n/JabRef_ja.properties | 4 --- src/main/resources/l10n/JabRef_ko.properties | 4 --- src/main/resources/l10n/JabRef_nl.properties | 4 --- src/main/resources/l10n/JabRef_pl.properties | 21 +++++++++-- src/main/resources/l10n/JabRef_pt.properties | 2 -- .../resources/l10n/JabRef_pt_BR.properties | 5 --- src/main/resources/l10n/JabRef_ru.properties | 8 ++--- src/main/resources/l10n/JabRef_sv.properties | 2 -- src/main/resources/l10n/JabRef_tr.properties | 4 --- src/main/resources/l10n/JabRef_uk.properties | 36 +++++++++++++++++++ src/main/resources/l10n/JabRef_vi.properties | 1 - .../resources/l10n/JabRef_zh_CN.properties | 4 --- .../resources/l10n/JabRef_zh_TW.properties | 1 - 18 files changed, 82 insertions(+), 62 deletions(-) diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index 9d6ee1620c2..770d0680f26 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -61,7 +61,7 @@ Added\ group\ "%0".=Gruppe "%0" hinzugefĆ¼gt. Added\ string\:\ '%0'=Zeichenkette hinzugefĆ¼gt\: '%0' Added\ string=String hinzugefĆ¼gt Edit\ strings=Strings bearbeiten -Duplicate\ string\ name\:\ '%0'=Doppelter String-Name\: '%0'l +Duplicate\ string\ name\:\ '%0'=Doppelter String-Name\: '%0' Modified\ string=VerƤnderter String Modified\ string\:\ '%0' =String geƤndert\: '%0' New\ string=Neuer String @@ -276,7 +276,6 @@ Donate\ to\ JabRef=An JabRef spenden Download\ file=Datei herunterladen -Downloaded\ website\ as\ an\ HTML\ file.=Webseite als HTML-Datei herunterladen. duplicate\ removal=Duplikate entfernen @@ -370,7 +369,6 @@ Manage\ field\ names\ &\ content=Feldnamen & Inhalt verwalten Field\ to\ group\ by=Sortierfeld Filter=Filter -Filter\ groups=Gruppen filtern Success\!\ Finished\ writing\ metadata.=Erfolgreich\! Das Schreiben der Metadaten ist abgeschlossen. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Fehler beim Schreiben von Metadaten. Details finden Sie im Fehlerprotokoll. @@ -1878,7 +1876,6 @@ Could\ not\ copy\ file=Datei konnte nicht kopiert werden Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=Kopierte %0 Dateien von %1 erfolgreich nach %2 Rename\ failed=Umbenennen fehlgeschlagen JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Konsolenausgabe anzeigen (nur wenn der Launcher verwendet wird) Remove\ line\ breaks=Entfernen der ZeilenumbrĆ¼che Removes\ all\ line\ breaks\ in\ the\ field\ content.=Entfernen aller ZeilenumbrĆ¼che im Inhalt des Feldes. @@ -2045,7 +2042,6 @@ No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=Es wurden keine LaTeX-Da Selected\ entry\ does\ not\ have\ an\ associated\ citation\ key.=Der ausgewƤhlte Eintrag hat keinen zugehƶrigen ZitationsschlĆ¼ssel. Current\ search\ directory\:=Aktuelles Suchverzeichnis\: Set\ LaTeX\ file\ directory=LaTeX-Dateiverzeichnis festlegen -Refresh=Aktualisieren Import\ entries\ from\ LaTeX\ files=EintrƤge aus LaTeX-Dateien importieren Import\ new\ entries=Neue EintrƤge importieren Group\ color=Gruppenfarbe @@ -2157,7 +2153,7 @@ Text\ editor=Text Editor Search\ ShortScience=Durchsuche ShortScience Unable\ to\ open\ ShortScience.=Kann keine Verbindung zu ShortScience herstellen. -Shared\ database=Geteilte SQL-Datenbank +Shared\ database=Geteilte Bibliothek (SQL) Lookup=Nachschlagen Access\ date\ of\ the\ address\ specified\ in\ the\ url\ field.=Zugriffsdatum der im URL-Feld angegebenen Adresse. @@ -2401,7 +2397,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=timestamp in Feld 'modi New\ entry\ by\ type=Neuer Eintrag nach Typ File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=Datei '%1' ist ein Duplikat von '%0'. Behalte '%0' -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=Datei '%1' ist ein Duplikat von '%0'. Beide werden aufgrund eines Lƶschfehlers beibehalten Enable\ field\ formatters=Feldformatierer aktivieren Entry\ Type=Eintragstyp diff --git a/src/main/resources/l10n/JabRef_el.properties b/src/main/resources/l10n/JabRef_el.properties index 344fc48add5..3a142fc59fb 100644 --- a/src/main/resources/l10n/JabRef_el.properties +++ b/src/main/resources/l10n/JabRef_el.properties @@ -261,7 +261,6 @@ Donate\ to\ JabRef=Ī”Ļ‰ĻĪµĪ¬ ĻƒĻ„Īæ JabRef Download\ file=ĪšĪ±Ļ„Ī­Ī²Ī±ĻƒĪ¼Ī± Ī±ĻĻ‡ĪµĪÆĪæĻ… -Downloaded\ website\ as\ an\ HTML\ file.=ĪŸĪ»ĪæĪŗĪ»Ī·ĻĻŽĪøĪ·ĪŗĪµ Ī· Ī»Ī®ĻˆĪ· Ļ„Ī·Ļ‚ Ī¹ĻƒĻ„ĪæĻƒĪµĪ»ĪÆĪ“Ī±Ļ‚ Ļ…Ļ€ĻŒ Ļ„Ī· Ī¼ĪæĻĻ†Ī® HTML Ī±ĻĻ‡ĪµĪÆĪæĻ…. duplicate\ removal=Ī“Ī¹Ī±Ī³ĻĪ±Ļ†Ī® Ī“Ī¹Ļ€Ī»ĻŒĻ„Ļ…Ļ€Ļ‰Ī½ @@ -333,7 +332,6 @@ Field\ names\ are\ not\ allowed\ to\ contain\ white\ spaces\ or\ certain\ charac Field\ to\ group\ by=ĪŸĪ¼Ī±Ī“ĪæĻ€ĪæĪÆĪ·ĻƒĪ· Ī¼Īµ Ļ„Īæ Ļ€ĪµĪ“ĪÆĪæ Filter=Ī¦ĪÆĪ»Ļ„ĻĪæ -Filter\ groups=Ī¦Ī¹Ī»Ļ„ĻĪ¬ĻĪ¹ĻƒĪ¼Ī± ĪæĪ¼Ī¬Ī“Ļ‰Ī½ Success\!\ Finished\ writing\ metadata.=Ī•Ļ€Ī¹Ļ„Ļ…Ļ‡ĪÆĪ±\! Ī— ĪµĪ³Ī³ĻĪ±Ļ†Ī® Ļ„Ļ‰Ī½ Ī¼ĪµĻ„Ī±Ī“ĪµĪ“ĪæĪ¼Ī­Ī½Ļ‰Ī½ ĪæĪ»ĪæĪŗĪ»Ī·ĻĻŽĪøĪ·ĪŗĪµ. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Ī£Ļ†Ī¬Ī»Ī¼Ī± ĪŗĪ±Ļ„Ī¬ Ļ„Ī·Ī½ ĪŗĪ±Ļ„Ī±Ī³ĻĪ±Ļ†Ī® Ī¼ĪµĻ„Ī±Ī“ĪµĪ“ĪæĪ¼Ī­Ī½Ļ‰Ī½. Ī‘Ī½Ī±Ļ„ĻĪ­Ī¾Ļ„Īµ ĻƒĻ„Īæ Ī¹ĻƒĻ„ĪæĻĪ¹ĪŗĻŒ ĻƒĻ†Ī±Ī»Ī¼Ī¬Ļ„Ļ‰Ī½ Ī³Ī¹Ī± Ī»ĪµĻ€Ļ„ĪæĪ¼Ī­ĻĪµĪ¹ĪµĻ‚. diff --git a/src/main/resources/l10n/JabRef_es.properties b/src/main/resources/l10n/JabRef_es.properties index e6caaddff68..1b7b209e39b 100644 --- a/src/main/resources/l10n/JabRef_es.properties +++ b/src/main/resources/l10n/JabRef_es.properties @@ -273,7 +273,6 @@ Donate\ to\ JabRef=Donar a JabRef Download\ file=Descargar archivo -Downloaded\ website\ as\ an\ HTML\ file.=Se descargĆ³ el sitio web como un archivo HTML. duplicate\ removal=eliminaciĆ³n de duplicados @@ -361,7 +360,6 @@ Manage\ field\ names\ &\ content=Gestionar nombres y contenido de los campos Field\ to\ group\ by=Agrupar a partir de campo Filter=Filtro -Filter\ groups=Filtros Success\!\ Finished\ writing\ metadata.=Ā”Ɖxito\! Se ha terminado de escribir metadatos. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Error al escribir los metadatos. Vea el registro de errores para mĆ”s detalles. @@ -1832,7 +1830,6 @@ Finished\ copying=Copia finalizada Could\ not\ copy\ file=No se puede copiar el archivo Rename\ failed=Error al renombrar JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef no puede acceder al archivo porque estĆ” siendo utilizado por otro proceso. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Mostrar la salida de la consola (sĆ³lo cuando se utiliza el lanzador) Remove\ line\ breaks=Eliminar saltos de lĆ­nea Removes\ all\ line\ breaks\ in\ the\ field\ content.=Eliminar todos los saltos de lĆ­nea en el contenido del campo. @@ -1996,7 +1993,6 @@ No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=No se han encontrado arc Selected\ entry\ does\ not\ have\ an\ associated\ citation\ key.=La entrada seleccionada no tiene asociada una clave de cita. Current\ search\ directory\:=Directorio de bĆŗsqueda actual\: Set\ LaTeX\ file\ directory=Establecer directorio de archivos LaTeX -Refresh=Actualizar Import\ entries\ from\ LaTeX\ files=Importar entradas desde archivos LaTeX Import\ new\ entries=Importar nuevas entradas Group\ color=Color del grupo @@ -2333,7 +2329,6 @@ Download\ operation\ canceled.=OperaciĆ³n de descarga cancelada. New\ entry\ by\ type=Entrada nueva por tipo File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=El archivo Ā«%1Ā» es un duplicado de Ā«%0Ā». Se conserva Ā«%0Ā» -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=El archivo Ā«%1Ā» es un duplicado de Ā«%0Ā». Se conservan ambos a causa de un error de eliminaciĆ³n Enable\ field\ formatters=Habilitar formateadores de campos Entry\ Type=Tipo de entrada diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index 6ad66b016ff..f08192f2c43 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -219,6 +219,7 @@ Custom\ entry\ types\ found\ in\ file=Types d'entrĆ©es personnalisĆ©es trouvĆ©es Customize\ entry\ types=Personnaliser les types d'entrĆ©es +Customize\ keyboard\ shortcuts=Personnaliser les raccourcis clavier Cut=Couper @@ -275,7 +276,6 @@ Donate\ to\ JabRef=Faire un don Ć  JabRef Download\ file=TĆ©lĆ©charger le fichier -Downloaded\ website\ as\ an\ HTML\ file.=Site web tĆ©lĆ©chargĆ© en tant que fichier HTML. duplicate\ removal=Suppression des doublons @@ -328,6 +328,10 @@ Export\ preferences\ to\ file=Exporter les prĆ©fĆ©rences vers un fichier Export\ to\ clipboard=Exporter vers le presse-papiers Export\ to\ text\ file.=Exporter vers un fichier texte. +Extract\ references\ from\ file\ (online)=Extraire les rĆ©fĆ©rences du fichier (en ligne) +Extract\ references\ from\ file\ (offline)=Extraire les rĆ©fĆ©rences du fichier (hors ligne) +Extract\ References\ (online)=Extraire les rĆ©fĆ©rences (en ligne) +Extract\ References\ (offline)=Extraire les rĆ©fĆ©rences (hors ligne) Processing\ PDF(s)=Traitement des PDF(s) en cours Processing\ a\ large\ number\ of\ files=Traitement d'un grand nombre de fichiers You\ are\ about\ to\ process\ %0\ files.\ Continue?=Vous ĆŖtes sur le point de traiter %0 fichiers. Continuer ? @@ -365,7 +369,6 @@ Manage\ field\ names\ &\ content=GĆ©rer les noms de champs et leur contenu Field\ to\ group\ by=Champ Ć  grouper par Filter=Choix des filtres -Filter\ groups=Filtrer les groupes Success\!\ Finished\ writing\ metadata.=SuccĆØs \! Ɖcriture des mĆ©tadonnĆ©es terminĆ©e. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Erreur lors de l'Ć©criture des mĆ©tadonnĆ©es. Consultez le journal des erreurs pour plus de dĆ©tails. @@ -506,7 +509,9 @@ Keep\ subgroups=Conserver les sous-groupes Key\ pattern=ParamĆ©trage des clefs +Keyboard\ shortcuts=Raccourcis clavier +Keyboard\ shortcuts\ changed=Raccourcis clavier modifiĆ©s keys\ in\ library=clefs dans le fichier @@ -1240,11 +1245,13 @@ You\ have\ to\ choose\ exactly\ two\ entries\ to\ merge.=Vous devez choisir exac Add\ timestamp\ to\ modified\ entries\ (field\ "modificationdate")=Ajouter un horodatage aux entrĆ©es modifiĆ©es (champ "modificationdate") Add\ timestamp\ to\ new\ entries\ (field\ "creationdate")=Ajouter un horodatage aux nouvelles entrĆ©es (champ "creationdate") +All\ keyboard\ shortcuts\ will\ be\ reset\ to\ their\ defaults.=Tous les raccourcis clavier seront rĆ©initialisĆ©s Ć  leurs valeurs par dĆ©faut. Automatically\ set\ file\ links=Configurer automatiquement les liens de fichier Finished\ automatically\ setting\ external\ links.=La dĆ©finition automatique des liens externes est terminĆ©e. Changed\ %0\ entries.=%0 entrĆ©es modifiĆ©es. +Resetting\ all\ keyboard\ shortcuts=RĆ©initialisation de tous les raccourcis clavier Open\ folder=Ouvrir le rĆ©pertoire Export\ sort\ order=Exporter l'ordre de tri @@ -1869,7 +1876,6 @@ Could\ not\ copy\ file=Le fichier n'a pas pu ĆŖtre copiĆ© Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=%0 fichiers sur %1 ont Ć©tĆ© copiĆ©s avec succĆØs vers %2 Rename\ failed=Ɖchec du renommage JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef ne peut pas accĆ©der au fichier parce qu'il est utilisĆ© par un autre processus. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Afficher la sortie de la console (uniquement nĆ©cessaire quand l'application est utilisĆ©e) Remove\ line\ breaks=Supprimer les sauts de ligne Removes\ all\ line\ breaks\ in\ the\ field\ content.=Supprime tous les sauts de ligne du contenu d'un champ. @@ -2036,7 +2042,6 @@ No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=Aucun fichier LaTeX cont Selected\ entry\ does\ not\ have\ an\ associated\ citation\ key.=L'entrĆ©e sĆ©lectionnĆ©e n'a pas de clef de citation associĆ©e. Current\ search\ directory\:=RĆ©pertoire de recherche actuel \: Set\ LaTeX\ file\ directory=Configurer le rĆ©pertoire des fichiers LaTeX -Refresh=RafraĆ®chir Import\ entries\ from\ LaTeX\ files=Importer des entrĆ©es Ć  partir des fichiers LaTeX Import\ new\ entries=Importer de nouvelles entrĆ©es Group\ color=Couleur du groupe @@ -2392,7 +2397,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=Convertir le champ 'tim New\ entry\ by\ type=Nouvelle entrĆ©e par type File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=Le fichier '%1' est un doublon de '%0'. Conservation de '%0' -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=Le fichier '%1' est un doublon de '%0'. Conservation des deux en raison d'une erreur de suppression Enable\ field\ formatters=Activer les formateurs de champs Entry\ Type=Type dā€™entrĆ©e @@ -2585,6 +2589,8 @@ Writing\ metadata\ to\ %0=Ɖcriture des mĆ©tadonnĆ©es sur %0 Get\ more\ themes...=Obtenir plus de thĆØmes... +Miscellaneous=Divers +File-related=LiĆ© au fichier Add\ selected\ entries\ to\ database=Ajouter les entrĆ©es sĆ©lectionnĆ©es au fichier The\ selected\ entry\ doesn't\ have\ a\ DOI\ linked\ to\ it.\ Lookup\ a\ DOI\ and\ try\ again.=L'entrĆ©e sĆ©lectionnĆ©e ne contient pas de DOI. Recherchez un DOI et rĆ©essayez. diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index 3a01e3b6340..5ad4fb78896 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -219,6 +219,7 @@ Custom\ entry\ types\ found\ in\ file=Tipi di voce personalizzati trovati nel fi Customize\ entry\ types=Personalizza tipi di voce +Customize\ keyboard\ shortcuts=Personalizza le scorciatoie da tastiera Cut=Taglia @@ -275,7 +276,6 @@ Donate\ to\ JabRef=Fai una donazione a JabRef Download\ file=Scarica il file -Downloaded\ website\ as\ an\ HTML\ file.=Sito web scaricato come file HTML. duplicate\ removal=rimozione di doppioni @@ -328,6 +328,10 @@ Export\ preferences\ to\ file=Esporta preferenze in un file Export\ to\ clipboard=Esporta negli appunti Export\ to\ text\ file.=Esporta su file di testo. +Extract\ references\ from\ file\ (online)=Estrai i riferimenti dal file (online) +Extract\ references\ from\ file\ (offline)=Estrai i riferimenti dal file (offline) +Extract\ References\ (online)=Estrai Riferimenti (online) +Extract\ References\ (offline)=Estrai i riferimenti dal file (offline) Processing\ PDF(s)=Elaborazione PDF Processing\ a\ large\ number\ of\ files=Elaborazione di un gran numero di file You\ are\ about\ to\ process\ %0\ files.\ Continue?=Stai per elaborare %0 file. Continuare? @@ -365,7 +369,6 @@ Manage\ field\ names\ &\ content=Gestione nomi di campi e contenuto Field\ to\ group\ by=Campo di raggruppamento Filter=Filtro -Filter\ groups=Filtra gruppi Success\!\ Finished\ writing\ metadata.=Completato con successo\! Scrivere i metadati. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Errore durante la scrittura dei metadati. Vedere il registro degli errori per i dettagli. @@ -506,7 +509,9 @@ Keep\ subgroups=Mantieni sottogruppi Key\ pattern=Modello delle chiavi +Keyboard\ shortcuts=Scorciatoie da tastiera +Keyboard\ shortcuts\ changed=Scorciatoie da tastiera cambiate keys\ in\ library=Chiavi nella libreria @@ -1240,11 +1245,13 @@ You\ have\ to\ choose\ exactly\ two\ entries\ to\ merge.=ƈ necessario seleziona Add\ timestamp\ to\ modified\ entries\ (field\ "modificationdate")=Aggiungi timestamp alle voci modificate (campo "modificationdate") Add\ timestamp\ to\ new\ entries\ (field\ "creationdate")=Aggiungi timestamp alle nuove voci (campo "creationdate") +All\ keyboard\ shortcuts\ will\ be\ reset\ to\ their\ defaults.=Tutte le scorciatoie da tastiera verranno reimpostate ai valori predefiniti. Automatically\ set\ file\ links=Impostazione automatica dei collegamenti ai file Finished\ automatically\ setting\ external\ links.=Impostazione automatica dei collegamenti esterni terminata. Changed\ %0\ entries.=Modificate %0 voci. +Resetting\ all\ keyboard\ shortcuts=Ripristino di tutte le scorciatoie da tastiera Open\ folder=Apri cartella Export\ sort\ order=Esporta il modo di ordinamento @@ -1869,7 +1876,6 @@ Could\ not\ copy\ file=Impossibile copiare il file Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=%0 file su %1 sono stati copiati correttamente in %2 Rename\ failed=Rinomina non riuscita JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef non puĆ² accedere al file perchĆ© questo ĆØ utilizzato da un altro processo. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Mostra l'output della console (solo quando viene utilizzato il launcher) Remove\ line\ breaks=Rimuovi interruzioni di riga Removes\ all\ line\ breaks\ in\ the\ field\ content.=Rimuove tutte le interruzioni di riga nel contenuto del campo. @@ -2036,7 +2042,6 @@ No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=Nessun file LaTeX conten Selected\ entry\ does\ not\ have\ an\ associated\ citation\ key.=La voce selezionata non ha una chiave BibTeX associata. Current\ search\ directory\:=Cartella di ricerca corrente\: Set\ LaTeX\ file\ directory=Imposta la cartella dei file LaTeX -Refresh=Aggiorna Import\ entries\ from\ LaTeX\ files=Importa le voci da file LaTeX Import\ new\ entries=Importa nuove voci Group\ color=Colore del gruppo @@ -2392,7 +2397,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=Converti il campo times New\ entry\ by\ type=Nuova voce per tipo File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=Il file '%1' ĆØ un duplicato di '%0'. Mantenere '%0' -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=Il file '%1' ĆØ un duplicato di '%0'. Mantenere entrambi a causa di errore di cancellazione Enable\ field\ formatters=Abilita formatori di campo Entry\ Type=Tipo di voce @@ -2585,6 +2589,8 @@ Writing\ metadata\ to\ %0=Scrittura dei metadati su %0 Get\ more\ themes...=Ottieni altri temi... +Miscellaneous=Varie +File-related=Correlato al file Add\ selected\ entries\ to\ database=Aggiungi le voci selezionate al database The\ selected\ entry\ doesn't\ have\ a\ DOI\ linked\ to\ it.\ Lookup\ a\ DOI\ and\ try\ again.=La voce selezionata non ha un DOI collegato ad essa. Cerca un DOI e riprova. diff --git a/src/main/resources/l10n/JabRef_ja.properties b/src/main/resources/l10n/JabRef_ja.properties index 8c590e3d08a..745f29b2877 100644 --- a/src/main/resources/l10n/JabRef_ja.properties +++ b/src/main/resources/l10n/JabRef_ja.properties @@ -263,7 +263,6 @@ Donate\ to\ JabRef=JabRef恫åƄ付 Download\ file=ćƒ•ć‚”ć‚¤ćƒ«ć‚’ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰ -Downloaded\ website\ as\ an\ HTML\ file.=ć‚¦ć‚§ćƒ–ć‚µć‚¤ćƒˆć‚’ HTML ćƒ•ć‚”ć‚¤ćƒ«ćØć—ć¦ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰ć—ć¾ć—ćŸļ¼Ž duplicate\ removal=é‡č¤‡ć‚’å‰Šé™¤ @@ -342,7 +341,6 @@ Manage\ field\ names\ &\ content=ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰åćƒ»ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰å€¤ć®ē®” Field\ to\ group\ by=ć‚°ćƒ«ćƒ¼ćƒ—åŒ–ć™ć‚‹ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ Filter=ćƒ•ć‚£ćƒ«ć‚æ -Filter\ groups=ć‚°ćƒ«ćƒ¼ćƒ—ēµžć‚Šč¾¼ćæ Success\!\ Finished\ writing\ metadata.=ęˆåŠŸć—ć¾ć—ćŸļ¼ćƒ”ć‚æćƒ‡ćƒ¼ć‚æ恮ę›øćč¾¼ćæ悒ēµ‚äŗ†ć—ć¾ć—ćŸļ¼Ž Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=惔ć‚æćƒ‡ćƒ¼ć‚æę›øćč¾¼ćæäø­ć«ć‚Øćƒ©ćƒ¼ļ¼Žč©³ē“°ć«ć¤ć„恦ćÆć‚Øćƒ©ćƒ¼ćƒ­ć‚°ć‚’å‚ē…§ć—ć¦ćć ć•ć„ļ¼Ž @@ -1761,7 +1759,6 @@ Finished\ copying=ć‚³ćƒ”ćƒ¼ć‚’ēµ‚äŗ†ć—ć¾ć—ćŸ Could\ not\ copy\ file=ćƒ•ć‚”ć‚¤ćƒ«ć‚’ć‚³ćƒ”ćƒ¼ć§ćć¾ć›ć‚“ć§ć—ćŸ Rename\ failed=ę”¹åć«å¤±ę•—ć—ć¾ć—ćŸ JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=ćƒ•ć‚”ć‚¤ćƒ«ćŒä»–ć®ćƒ—ćƒ­ć‚»ć‚¹ć«ć‚ˆć£ć¦ä½æē”Øć•ć‚Œć¦ć„ć‚‹ćŸć‚ļ¼ŒJabRefćŒć‚¢ć‚Æć‚»ć‚¹ć™ć‚‹ć“ćØćŒć§ćć¾ć›ć‚“ļ¼Ž -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=ć‚³ćƒ³ć‚½ćƒ¼ćƒ«å‡ŗåŠ›ć‚’č”Øē¤ŗļ¼ˆćƒ­ćƒ³ćƒćƒ£ćƒ¼ć‚’ä½æē”Øć™ć‚‹å “åˆć®ćæļ¼‰ Remove\ line\ breaks=ę”¹č”Œć‚’å–ć‚Šé™¤ć Removes\ all\ line\ breaks\ in\ the\ field\ content.=ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ć®å†…å®¹ć‹ć‚‰ę”¹č”Œć‚’å…Øć¦å–ć‚Šé™¤ćć¾ć™ļ¼Ž @@ -2261,7 +2258,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=timestampćƒ•ć‚£ćƒ¼ćƒ« New\ entry\ by\ type=åž‹åˆ„ć®ę–°č¦é …ē›® File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=ćƒ•ć‚”ć‚¤ćƒ«ć€Œ%1怍ćÆ怌%0怍ćØé‡č¤‡ć—ć¦ć„ć¾ć™ļ¼Žć€Œ%0怍悒äæęŒć—ć¾ć™ -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=ćƒ•ć‚”ć‚¤ćƒ«ć€Œ%1怍ćÆ怌%0怍ćØé‡č¤‡ć—ć¦ć„ć¾ć™ļ¼Žå‰Šé™¤ć‚Øćƒ©ćƒ¼ćŒčµ·ć“ć‚Šć¾ć—ćŸć®ć§äø”ę–¹äæęŒć—ć¾ć™ Enable\ field\ formatters=ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ę•“å½¢ć®å®šē¾©ć‚’ęœ‰åŠ¹ć«ć™ć‚‹ Entry\ Type=項ē›®åž‹ diff --git a/src/main/resources/l10n/JabRef_ko.properties b/src/main/resources/l10n/JabRef_ko.properties index a82bd22c4c7..de01984d61c 100644 --- a/src/main/resources/l10n/JabRef_ko.properties +++ b/src/main/resources/l10n/JabRef_ko.properties @@ -250,7 +250,6 @@ Donate\ to\ JabRef=JabRefģ— źø°ė¶€ķ•˜źø° Download\ file=ķŒŒģ¼ ė‹¤ģš“ė”œė“œ -Downloaded\ website\ as\ an\ HTML\ file.=ģ›¹ģ‚¬ģ“ķŠøė„¼ HTML ķŒŒģ¼ė”œ ė‹¤ģš“ė”œė“œķ–ˆģŠµė‹ˆė‹¤. duplicate\ removal=ģ¤‘ė³µ ģ œź±° @@ -327,7 +326,6 @@ Manage\ field\ names\ &\ content=ķ•„ė“œ ģ“ė¦„ ė° ģ½˜ķ…ģø  ź“€ė¦¬ Field\ to\ group\ by=ķ•„ė“œė„¼ ź·øė£¹ķ™” ķ•˜ė‹¤ Filter=ķ•„ķ„° -Filter\ groups=ź·øė£¹ ķ•„ķ„°ė§ Generate=ģƒģ„± @@ -1670,7 +1668,6 @@ Finished\ copying=ė³µģ‚¬ ģ™„ė£Œ Could\ not\ copy\ file=ķŒŒģ¼ģ„ ė³µģ‚¬ķ•  ģˆ˜ ģ—†ģŠµė‹ˆė‹¤ Rename\ failed=ģ“ė¦„ ė³€ź²½ ģ‹¤ķŒØ JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRefėŠ” ė‹¤ė„ø ķ”„ė”œģ„øģŠ¤ģ—ģ„œ ģ‚¬ģš© ģ¤‘ģø ķŒŒģ¼ģ— ģ ‘ź·¼ķ•  ģˆ˜ ģ—†ģŠµė‹ˆė‹¤ -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=ģ½˜ģ†” ģ¶œė „ ķ‘œģ‹œ(ėŸ°ģ²˜ė„¼ ģ‚¬ģš©ķ•˜ėŠ” ź²½ģš°ģ—ė§Œ) Remove\ line\ breaks=ģ¤„ė°”źæˆ ģ œź±° Removes\ all\ line\ breaks\ in\ the\ field\ content.=ķ•„ė“œ ė‚“ģš©ģ—ģ„œ ėŖØė“  ģ¤„ ė°”źæˆģ„ ģ œź±°ķ•©ė‹ˆė‹¤. @@ -2149,7 +2146,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=ķƒ€ģž„ģŠ¤ķƒ¬ķ”„ ķ•„ė“œ New\ entry\ by\ type=ģœ ķ˜•ė³„ ģƒˆ ķ•­ėŖ© File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=%1 ķŒŒģ¼ģ€ %0ź³¼ ė™ģ¼ķ•©ė‹ˆė‹¤. %0ģ„ ģœ ģ§€ķ•©ė‹ˆė‹¤. -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=%1 ķŒŒģ¼ģ€ %0ź³¼ ė™ģ¼ķ•©ė‹ˆė‹¤. \nģ‚­ģ œ ģ˜¤ė„˜ė”œ ģøķ•“ ė‘ ķŒŒģ¼ģ„ ģœ ģ§€ķ•©ė‹ˆė‹¤. Enable\ field\ formatters=ķ•„ė“œ ķ¬ė§·ķ„° ķ™œģ„±ķ™” Entry\ Type=ėŖ©ė” ģœ ķ˜• diff --git a/src/main/resources/l10n/JabRef_nl.properties b/src/main/resources/l10n/JabRef_nl.properties index 5e56cbf18e2..c9c575a3c56 100644 --- a/src/main/resources/l10n/JabRef_nl.properties +++ b/src/main/resources/l10n/JabRef_nl.properties @@ -267,7 +267,6 @@ Donate\ to\ JabRef=Doneer aan JabRef Download\ file=Download bestand -Downloaded\ website\ as\ an\ HTML\ file.=Gedownloade website als een HTML-bestand. duplicate\ removal=duplicaten verwijderen @@ -348,7 +347,6 @@ Manage\ field\ names\ &\ content=Beheer veldnamen & inhoud Field\ to\ group\ by=Veld te groeperen op Filter=Filteren -Filter\ groups=Filter groepen Success\!\ Finished\ writing\ metadata.=Gelukt\! Metagegevens schrijven voltooid. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Fout tijdens het schrijven van metadata. Bekijk het foutenlogboek voor meer informatie. @@ -1824,7 +1822,6 @@ Could\ not\ copy\ file=Kan het bestand niet kopiĆ«ren Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=%0 bestanden van %1 succesvol gekopieerd naar %2 Rename\ failed=Hernoemen mislukt JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef kan geen toegang krijgen tot dit bestand omdat het door een ander proces wordt gebruikt. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Toon console-uitvoer (alleen wanneer de launcher wordt gebruikt) Remove\ line\ breaks=Verwijder regeleinden Removes\ all\ line\ breaks\ in\ the\ field\ content.=Verwijder alle regeleinden in de veldinhoud. @@ -2332,7 +2329,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=Converteer tijdsstempel New\ entry\ by\ type=Nieuwe invoer per type File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=Bestand '%1' is een duplicaat van '%0'. Behoud '%0' -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=Bestand '%1' is een duplicaat van '%0'. Beide behouden omdat de fout bij het verwijderen is Enable\ field\ formatters=Veld formatteerder inschakelen Entry\ Type=Invoer Type diff --git a/src/main/resources/l10n/JabRef_pl.properties b/src/main/resources/l10n/JabRef_pl.properties index a83d5f1fcc3..90ccfa12663 100644 --- a/src/main/resources/l10n/JabRef_pl.properties +++ b/src/main/resources/l10n/JabRef_pl.properties @@ -69,6 +69,7 @@ Resolve\ BibTeX\ strings=Rozwiąż ciągi znakĆ³w BibTeX The\ label\ of\ the\ string\ cannot\ be\ a\ number.=Etykieta ciągu znakĆ³w nie może być liczbą. The\ label\ of\ the\ string\ cannot\ contain\ spaces.=Etykieta ciągu znakĆ³w nie może zawierać spacji. The\ label\ of\ the\ string\ cannot\ contain\ the\ '\#'\ character.=Etykieta ciągu znakĆ³w nie może zawierać znaku '\#'. +A\ string\ with\ the\ label\ '%0'\ already\ exists.=Ciąg znakĆ³w z etykietą '%0' już istnieje. All\ entries=Wszystkie wpisy @@ -202,6 +203,7 @@ Custom\ entry\ types\ found\ in\ file=Znaleziono niestandardowe typy wpisĆ³w w p Customize\ entry\ types=Dostosuj typy wpisĆ³w +Customize\ keyboard\ shortcuts=Dostosuj skrĆ³ty klawiaturowe Cut=Wytnij @@ -258,7 +260,6 @@ Donate\ to\ JabRef=WspomĆ³Å¼ JabRef Download\ file=Pobierz plik -Downloaded\ website\ as\ an\ HTML\ file.=Pobrano stronę internetową jako plik HTML. duplicate\ removal=usuwanie duplikatĆ³w @@ -311,6 +312,10 @@ Export\ preferences\ to\ file=Eksportuj ustawienia do pliku Export\ to\ clipboard=Eksportuj do schowka Export\ to\ text\ file.=Eksportuj do pliku tekstowego. +Extract\ references\ from\ file\ (online)=Wyodrębnij referencje z pliku (online) +Extract\ references\ from\ file\ (offline)=Wyodrębnij referencje z pliku (offline) +Extract\ References\ (online)=Wyodrębnij referencje (online) +Extract\ References\ (offline)=Wyodrębnij referencje (offline) Processing\ PDF(s)=Przetwarzanie PDF(Ć³w) Processing\ a\ large\ number\ of\ files=Przetwarzanie dużej liczby plikĆ³w You\ are\ about\ to\ process\ %0\ files.\ Continue?=Zamierzasz przetworzyć %0 plikĆ³w. Czy chcesz kontynuować? @@ -348,7 +353,6 @@ Manage\ field\ names\ &\ content=Zarządzaj nazwami pĆ³l i ich zawartością Field\ to\ group\ by=Pole do grupowania według Filter=Filtruj -Filter\ groups=Filtruj grupy Success\!\ Finished\ writing\ metadata.=Sukces\! Zakończono zapisywanie metadanych. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Błąd podczas zapisywania metadanych. Aby uzyskać szczegĆ³Å‚owe informacje, zobacz dziennik błędĆ³w. @@ -366,6 +370,7 @@ Generated\ citation\ key\ for=Wygenerowano klucz cytowania dla Generating\ citation\ key\ for=Generowanie klucza cytowania dla Invalid\ citation\ key=Nieprawidłowy klucz cytowania +Jump\ to\ entry\ in\ library=PrzejdÅŗ do wpisu w bibliotece Autolink\ files\ with\ names\ starting\ with\ the\ citation\ key=Automatycznie podpinaj pliki z nazwami zaczynającymi się od klucza cytowania Autolink\ only\ files\ that\ match\ the\ citation\ key=Automatycznie podpinaj tylko pliki, ktĆ³re pasują do klucza cytowania @@ -435,6 +440,7 @@ Online\ help=Pomoc online JabRef\ Language\ (Provides\ for\ better\ recommendations\ by\ giving\ an\ indication\ of\ user's\ preferred\ language.)=Język JabRef (zapewnia lepsze rekomendacje poprzez wskazanie preferowanego języka użytkownika). JabRef\ preferences=Preferencje JabRef +JabRef\ Version\ (Required\ to\ ensure\ backwards\ compatibility\ with\ Mr.\ DLib's\ Web\ Service)=Wersja JabRef (wymagana do zapewnienia kompatybilności wstecznej z Mr. DLib's Web Service) Journal\ abbreviations=SkrĆ³ty czasopism Journal\ lists\:=Listy czasopism\: @@ -468,7 +474,9 @@ Keep\ subgroups=Zachowaj podgrupy Key\ pattern=Układ klawiszy +Keyboard\ shortcuts=SkrĆ³ty klawiaturowe +Keyboard\ shortcuts\ changed=Zmieniono skrĆ³ty klawiaturowe keys\ in\ library=klucze w bibliotece @@ -768,7 +776,10 @@ Empty\ search\ ID=Pusty identyfikator wyszukiwania The\ given\ search\ ID\ was\ empty.=Podany identyfikator wyszukiwania był pusty. Clear\ search=Wyczyść wyszukiwanie Search\ document\ identifier\ online=Szukaj identyfikatora dokumentu online +Search\ for\ unlinked\ local\ files=Wyszukaj niepowiązane pliki lokalne +Search\ full\ text\ documents\ online=Wyszukaj pełne teksty dokumentĆ³w online Search\ term\ is\ empty.=Szukane wyrażenie jest puste. +Invalid\ regular\ expression.=Nieprawidłowe wyrażenie regularne. Searching\ for\ a\ keyword=Wyszukiwanie słowa kluczowego Search\ across\ libraries\ in\ a\ new\ window=Szukaj w bibliotekach, wynik pokaż w nowym oknie @@ -797,6 +808,7 @@ Show\ BibTeX\ source\ by\ default=Domyślnie pokaż ÅŗrĆ³dło BibTeX Attached\ files=Załączone pliki Show\ confirmation\ dialog\ when\ deleting\ entries=Pokaż okno dialogowe potwierdzenia podczas usuwania wpisĆ³w +Show\ confirmation\ dialog\ when\ deleting\ attached\ files=Pokaż okno dialogowe potwierdzenia podczas usuwania plikĆ³w Persist\ password\ between\ sessions=Zachowaj hasło między sesjami @@ -1130,9 +1142,11 @@ Parse=Parsuj Result=Rezultat You\ have\ to\ choose\ exactly\ two\ entries\ to\ merge.=Musisz wybrać dokładnie dwa wpisy do scalenia. +All\ keyboard\ shortcuts\ will\ be\ reset\ to\ their\ defaults.=Wszystkie skrĆ³ty klawiaturowe zostaną zresetowane do wartości domyślnych. Changed\ %0\ entries.=Zmieniono %0 wpisĆ³w. +Resetting\ all\ keyboard\ shortcuts=Resetowanie wszystkich skrĆ³tĆ³w klawiaturowych Open\ folder=OtwĆ³rz folder Export\ sort\ order=Eksportuj kolejność sortowania @@ -1578,7 +1592,6 @@ files=pliki No\ citations\ found=Nie znaleziono cytatĆ³w Current\ search\ directory\:=Aktualny katalog wyszukiwania\: -Refresh=Odśwież Import\ entries\ from\ LaTeX\ files=Importuj wpisy z plikĆ³w LaTeX Import\ new\ entries=Importuj nowe wpisy @@ -1744,6 +1757,8 @@ Finished\ writing\ metadata\ for\ library\ %0\ (%1\ succeeded,\ %2\ skipped,\ %3 Processing...=Przetwarzanie... +Miscellaneous=Pozostałe +File-related=Związane z plikami Cancel\ search=Anuluj wyszukiwanie Select\ entry=Wybierz wpis diff --git a/src/main/resources/l10n/JabRef_pt.properties b/src/main/resources/l10n/JabRef_pt.properties index 03189da0ac7..6a496f87516 100644 --- a/src/main/resources/l10n/JabRef_pt.properties +++ b/src/main/resources/l10n/JabRef_pt.properties @@ -261,7 +261,6 @@ Donate\ to\ JabRef=Doar para JabRef Download\ file=Download do arquivo -Downloaded\ website\ as\ an\ HTML\ file.=Site baixado como arquivo HTML. duplicate\ removal=remoĆ§Ć£o de duplicatas @@ -338,7 +337,6 @@ Manage\ field\ names\ &\ content=Gerenciar nomes e conteĆŗdo dos campos Field\ to\ group\ by=Campos como critĆ©rio de agrupamento Filter=Filtro -Filter\ groups=Filtrar grupos Success\!\ Finished\ writing\ metadata.=Sucesso\! Escrita de metadados finalizada. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Erro ao escrever metadados. Consulte o log de erros para obter detalhes. diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index 94fed9e733e..7f7fdc2cfaf 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -271,7 +271,6 @@ Donate\ to\ JabRef=Doar para JabRef Download\ file=Download do arquivo -Downloaded\ website\ as\ an\ HTML\ file.=Site baixado como arquivo HTML. duplicate\ removal=remoĆ§Ć£o de duplicatas @@ -361,7 +360,6 @@ Manage\ field\ names\ &\ content=Gerenciar nomes e conteĆŗdo dos campos Field\ to\ group\ by=Campos como critĆ©rio de agrupamento Filter=Filtro -Filter\ groups=Filtrar grupos Success\!\ Finished\ writing\ metadata.=Sucesso\! Terminou a escrita de metadados. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Erro ao escrever metadados. Consulte o log de erros para obter detalhes. @@ -1864,7 +1862,6 @@ Could\ not\ copy\ file=NĆ£o foi possĆ­vel copiar o arquivo Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=%0 arquivos de %1 copiados com sucesso para %2 Rename\ failed=Falha ao renomear JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef nĆ£o pode acessar o arquivo porque ele estĆ” sendo usado por outro processo. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Mostrar saĆ­da do console (somente quando o launcher Ć© usado) Remove\ line\ breaks=Remover quebras de linha Removes\ all\ line\ breaks\ in\ the\ field\ content.=Remove todas as quebras de linha do conteĆŗdo do campo. @@ -2031,7 +2028,6 @@ No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=Nenhum arquivo LaTeX con Selected\ entry\ does\ not\ have\ an\ associated\ citation\ key.=O item selecionado nĆ£o tem uma chave de citaĆ§Ć£o associada. Current\ search\ directory\:=DiretĆ³rio de pesquisa atual\: Set\ LaTeX\ file\ directory=Definir diretĆ³rio de arquivos LaTeX -Refresh=Atualizar Import\ entries\ from\ LaTeX\ files=Importar referĆŖncias a partir dos arquivos LaTeX Import\ new\ entries=Importar novas referĆŖncias Group\ color=Cor do grupo @@ -2387,7 +2383,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=Converter campo timesta New\ entry\ by\ type=Nova entrada por tipo File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=Arquivo '%1' Ć© uma duplicata de '%0'. Mantendo '%0' -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=Arquivo '%1' Ć© uma duplicata de '%0'. Mantendo ambos devido a um erro de exclusĆ£o Enable\ field\ formatters=Ativar formatadores de campo Entry\ Type=Tipo de referĆŖncia diff --git a/src/main/resources/l10n/JabRef_ru.properties b/src/main/resources/l10n/JabRef_ru.properties index 1b92c78994a..bfbf49c6868 100644 --- a/src/main/resources/l10n/JabRef_ru.properties +++ b/src/main/resources/l10n/JabRef_ru.properties @@ -255,7 +255,6 @@ Donate\ to\ JabRef=ŠŸŠ¾Š“Š“ŠµŃ€Š¶Š°Ń‚ŃŒ JabRef Download\ file=Š—Š°Š³Ń€ŃƒŠ·Šøть фŠ°Š¹Š» -Downloaded\ website\ as\ an\ HTML\ file.=Š—Š°Š³Ń€ŃƒŠ¶ŠµŠ½ сŠ°Š¹Ń‚ Š² фŠ¾Ń€Š¼Š°Ń‚Šµ HTML. duplicate\ removal=уŠ“Š°Š»ŠµŠ½ŠøŠµ Š“уŠ±Š»ŠøŠŗŠ°Ń‚Š¾Š² @@ -334,7 +333,6 @@ Manage\ field\ names\ &\ content=Š£ŠæрŠ°Š²Š»ŃŃ‚ŃŒ Š½Š°Š·Š²Š°Š½ŠøяŠ¼Šø ŠæŠ¾Š» Field\ to\ group\ by=ŠŸŠ¾Š»Šµ Š“Š»Ń Š³Ń€ŃƒŠæŠæŠøрŠ¾Š²ŠŗŠø Filter=Š¤ŠøŠ»ŃŒŃ‚Ń€ -Filter\ groups=Š¤ŠøŠ»ŃŒŃ‚Ń€Š¾Š²Š°Ń‚ŃŒ Š³Ń€ŃƒŠæŠæы Success\!\ Finished\ writing\ metadata.=Š£ŃŠæŠµŃ…\! Š—Š°ŠæŠøсь Š¼ŠµŃ‚Š°Š“Š°Š½Š½Ń‹Ń… Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š°. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=ŠžŃˆŠøŠ±ŠŗŠ° ŠæрŠø Š·Š°ŠæŠøсŠø Š¼ŠµŃ‚Š°Š“Š°Š½Š½Ń‹Ń…. Š”Š¼Š¾Ń‚Ń€ŠøтŠµ Š¶ŃƒŃ€Š½Š°Š» Š¾ŃˆŠøŠ±Š¾Šŗ Š“Š»Ń ŠæŠ¾Š“рŠ¾Š±Š½Š¾ŃŃ‚ŠµŠ¹. @@ -442,6 +440,10 @@ Journal\ abbreviations=Š”Š¾ŠŗрŠ°Ń‰ŠµŠ½Šøя Š“Š»Ń Š¶ŃƒŃ€Š½Š°Š»Š¾Š² Journal\ lists\:=Š”ŠæŠøсŠ¾Šŗ Š¶ŃƒŃ€Š½Š°Š»Š¾Š²\: Remove\ journal\ '%0'=Š£Š“Š°Š»Šøть Š¶ŃƒŃ€Š½Š°Š» '%0' +Year=Š“Š¾Š“ +Categories=ŠšŠ°Ń‚ŠµŠ³Š¾Ń€ŠøŠø +ISSN=ISSN +Publisher=Š˜Š·Š“Š°Ń‚ŠµŠ»ŃŒ Keep\ both=ŠžŃŃ‚Š°Š²Šøть Š¾Š±Š° @@ -1729,7 +1731,6 @@ Finished\ copying=ŠšŠ¾ŠæŠøрŠ¾Š²Š°Š½ŠøŠµ Š·Š°Š²ŠµŃ€ŃˆŠµŠ½Š¾ Could\ not\ copy\ file=ŠŠµŠ²Š¾Š·Š¼Š¾Š¶Š½Š¾ сŠŗŠ¾ŠæŠøрŠ¾Š²Š°Ń‚ŃŒ фŠ°Š¹Š» Rename\ failed=ŠŠµ уŠ“Š°Š»Š¾ŃŃŒ ŠæŠµŃ€ŠµŠøŠ¼ŠµŠ½Š¾Š²Š°Ń‚ŃŒ JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef Š½Šµ Š¼Š¾Š¶ŠµŃ‚ ŠæŠ¾Š»ŃƒŃ‡Šøть Š“Š¾ŃŃ‚ŃƒŠæ Šŗ фŠ°Š¹Š»Ńƒ, тŠ°Šŗ ŠŗŠ°Šŗ Š¾Š½ ŠøсŠæŠ¾Š»ŃŒŠ·ŃƒŠµŃ‚ся Š“руŠ³ŠøŠ¼ ŠæрŠ¾Ń†ŠµŃŃŠ¾Š¼. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=ŠŸŠ¾ŠŗŠ°Š·Ń‹Š²Š°Ń‚ŃŒ Š²Ń‹Š²Š¾Š“ ŠŗŠ¾Š½ŃŠ¾Š»Šø (тŠ¾Š»ŃŒŠŗŠ¾ ŠæрŠø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø Š»Š°ŃƒŠ½Ń‡ŠµŃ€Š°) Remove\ line\ breaks=Š£Š“Š°Š»Šøть рŠ°Š·Ń€Ń‹Š²Ń‹ стрŠ¾Šŗ Removes\ all\ line\ breaks\ in\ the\ field\ content.=Š£Š“Š°Š»ŃŠµŃ‚ Š²ŃŠµ рŠ°Š·Ń€Ń‹Š²Ń‹ стрŠ¾Šŗ Š² сŠ¾Š“ŠµŃ€Š¶ŠøŠ¼Š¾Š¼ ŠæŠ¾Š»Ń. @@ -2226,7 +2227,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=ŠŸŃ€ŠµŠ¾Š±Ń€Š°Š·Š¾Š²Š° New\ entry\ by\ type=ŠŠ¾Š²Š°Ń Š·Š°ŠæŠøсь ŠæŠ¾ тŠøŠæу File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=Š¤Š°Š¹Š» '%1' яŠ²Š»ŃŠµŃ‚ся Š“уŠ±Š»ŠøŠŗŠ°Ń‚Š¾Š¼ '%0'. ŠžŃŃ‚Š°Š²Š»ŠµŠ½ '%0' -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=Š¤Š°Š¹Š» '%1' яŠ²Š»ŃŠµŃ‚ся Š“уŠ±Š»ŠøŠŗŠ°Ń‚Š¾Š¼ '%0'. ŠžŃŃ‚Š°Š²Š»ŠµŠ½Ń‹ Š¾Š±Š° ŠøŠ·-Š·Š° Š¾ŃˆŠøŠ±ŠŗŠø уŠ“Š°Š»ŠµŠ½Šøя Enable\ field\ formatters=Š’ŠŗŠ»ŃŽŃ‡Šøть фŠ¾Ń€Š¼Š°Ń‚ŠøрŠ¾Š²Š°Š½ŠøŠµ ŠæŠ¾Š»ŠµŠ¹ Entry\ Type=Š¢ŠøŠæ Š·Š°ŠæŠøсŠø diff --git a/src/main/resources/l10n/JabRef_sv.properties b/src/main/resources/l10n/JabRef_sv.properties index 569743a1f1c..e4ba746185c 100644 --- a/src/main/resources/l10n/JabRef_sv.properties +++ b/src/main/resources/l10n/JabRef_sv.properties @@ -249,7 +249,6 @@ Donate\ to\ JabRef=Donera till JabRef Download\ file=Ladda ned fil -Downloaded\ website\ as\ an\ HTML\ file.=Webbsida nedladdad som html-fil. duplicate\ removal=ta bort dubbletter @@ -328,7 +327,6 @@ Manage\ field\ names\ &\ content=Hantera fƤltnamn & innehĆ„ll Field\ to\ group\ by=FƤlt att anvƤnda fƶr gruppering Filter=Filtrera -Filter\ groups=Filtrera grupper Generate=Generera diff --git a/src/main/resources/l10n/JabRef_tr.properties b/src/main/resources/l10n/JabRef_tr.properties index abb65c2f6ae..bfac7f74ea2 100644 --- a/src/main/resources/l10n/JabRef_tr.properties +++ b/src/main/resources/l10n/JabRef_tr.properties @@ -269,7 +269,6 @@ Donate\ to\ JabRef=JabRef'e bağış yap Download\ file=Dosya indir -Downloaded\ website\ as\ an\ HTML\ file.=Web sitesi bir HTML dosyası olarak indirildi. duplicate\ removal=Ƨift nĆ¼sha silme @@ -356,7 +355,6 @@ Manage\ field\ names\ &\ content=Alan ad ve iƧeriklerini yƶnet Field\ to\ group\ by=Gruplanacak alan Filter=SĆ¼zgeƧ -Filter\ groups=Grupları filtrele Success\!\ Finished\ writing\ metadata.=Başarılı\! Metaverisi yazma bitirildi. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=Metaverisi yazılırken hata. Ayrıntılar iƧin hata kayıtlarına bakın. @@ -1843,7 +1841,6 @@ Could\ not\ copy\ file=Dosya kopyalanamadı Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=%1 dosyanın %0'i başarıyla %2'e kopyalandı Rename\ failed=Yeniden adlandırma başarısız oldu JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef dosyaya erişemiyor Ć§Ć¼nkĆ¼ dosya başka bir sĆ¼reƧ tarafından kullanılıyor. -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Konsol Ƨıktısını gƶster (yalnızca başlatıcı kullanıldığında) Remove\ line\ breaks=Satır sonlarını kaldır Removes\ all\ line\ breaks\ in\ the\ field\ content.=Alan iƧeriğindeki tĆ¼m satır sonlarını kaldırır. @@ -2363,7 +2360,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=Tarih bilgisi alanını New\ entry\ by\ type=TĆ¼rle yeni girdi File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'='%1' dosyası '%0'ın tıpkısı. '%0' tutuluyor -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error='%1' dosyası '%0'ın tıpkısı. Silme hatası nedeniyle ikisi de tutuluyor Enable\ field\ formatters=Alan biƧimlendiricilerini etkinleştir Entry\ Type=Girdi TĆ¼rĆ¼ diff --git a/src/main/resources/l10n/JabRef_uk.properties b/src/main/resources/l10n/JabRef_uk.properties index 5ccd342ef5d..b39e7976f6b 100644 --- a/src/main/resources/l10n/JabRef_uk.properties +++ b/src/main/resources/l10n/JabRef_uk.properties @@ -60,6 +60,7 @@ Added\ group\ "%0".=Š”Š¾Š“Š°Š½Š° Š³Ń€ŃƒŠæŠ° Added\ string\:\ '%0'=Š”Š¾Š“Š°Š½Š¾ ряŠ“Š¾Šŗ Added\ string=Š”Š¾Š“Š°Š½Š¾ ряŠ“Š¾Šŗ +Must\ not\ be\ empty\!=ŠŠµ Š¼Š¾Š¶Šµ Š±ŃƒŃ‚Šø ŠæŠ¾Ń€Š¾Š¶Š½Ń–Š¼\! All\ entries=Š’сі Š·Š°ŠæŠøсŠø @@ -90,12 +91,20 @@ Application\ to\ push\ entries\ to=Š—Š°ŃŠ²ŠŗŠ° Š½Š° push Š¼Š°Ń‚ŠµŃ€Ń–Š°Š»Ń–Š² +Cancel=Š”ŠŗŠ°ŃŃƒŠ²Š°Ń‚Šø +Cannot\ create\ group=ŠŠµ Š²Š“Š°Ń”Ń‚ŃŒŃŃ стŠ²Š¾Ń€ŠøтŠø Š³Ń€ŃƒŠæу +Cannot\ create\ group.\ Please\ create\ a\ library\ first.=ŠŠµ Š²Š“Š°Ń”Ń‚ŃŒŃŃ стŠ²Š¾Ń€ŠøтŠø Š³Ń€ŃƒŠæу. Š‘ŃƒŠ“ь Š»Š°ŃŠŗŠ°, сŠæŠ¾Ń‡Š°Ń‚Šŗу стŠ²Š¾Ń€Ń–Ń‚ŃŒ Š±Ń–Š±Š»Ń–Š¾Ń‚ŠµŠŗу. +Cannot\ open\ folder\ as\ the\ file\ is\ an\ online\ link.=ŠŠµ Š²Š“Š°Ń”Ń‚ŃŒŃŃ Š²Ń–Š“ŠŗрŠøтŠø ŠæŠ°ŠæŠŗу, Š¾ŃŠŗіŠ»ŃŒŠŗŠø фŠ°Š¹Š» є ŠæŠ¾ŃŠøŠ»Š°Š½Š½ŃŠ¼. +case\ insensitive=Š±ŠµŠ· урŠ°Ń…ŃƒŠ²Š°Š½Š½Ń рŠµŠ³Ń–ŃŃ‚Ń€Ńƒ +case\ sensitive=Š· урŠ°Ń…ŃƒŠ²Š°Š½Š½ŃŠ¼ рŠµŠ³Ń–ŃŃ‚Ń€Ńƒ +Case\ sensitive=Š— урŠ°Ń…ŃƒŠ²Š°Š½Š½ŃŠ¼ рŠµŠ³Ń–ŃŃ‚Ń€Ńƒ +change\ assignment\ of\ entries=Š·Š¼Ń–Š½ŠøтŠø ŠæрŠøŠ·Š½Š°Ń‡ŠµŠ½Š½Ń Š·Š°ŠæŠøсіŠ² @@ -107,24 +116,51 @@ Application\ to\ push\ entries\ to=Š—Š°ŃŠ²ŠŗŠ° Š½Š° push Š¼Š°Ń‚ŠµŃ€Ń–Š°Š»Ń–Š² +Open\ /\ close\ entry\ editor=Š’Ń–Š“ŠŗрŠøтŠø / Š·Š°ŠŗрŠøтŠø рŠµŠ“Š°ŠŗтŠ¾Ń€ Š·Š°ŠæŠøсіŠ² +Close\ dialog=Š—Š°ŠŗрŠøтŠø Š“іŠ°Š»Š¾Š³ +Close\ the\ current\ library=Š—Š°ŠŗрŠøтŠø ŠæŠ¾Ń‚Š¾Ń‡Š½Ńƒ Š±Ń–Š±Š»Ń–Š¾Ń‚ŠµŠŗу +Close\ window=Š—Š°ŠŗрŠøтŠø Š²Ń–ŠŗŠ½Š¾ +Comments=ŠšŠ¾Š¼ŠµŠ½Ń‚Š°Ń€Ń– +Contained\ in=ŠœŃ–стŠøться Š² +Content=Š—Š¼Ń–ст +Copy=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø +Copy\ title=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø Š·Š°Š³Š¾Š»Š¾Š²Š¾Šŗ +Copy\ citation\ (html)=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø цŠøтуŠ²Š°Š½Š½Ń (html) +Copy\ citation\ (text)=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø цŠøтуŠ²Š°Š½Š½Ń (тŠµŠŗст) +Copy\ citation\ key=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø ŠŗŠ»ŃŽŃ‡ цŠøтуŠ²Š°Š½Š½Ń +Copy\ citation\ key\ and\ link=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø ŠŗŠ»ŃŽŃ‡ цŠøтуŠ²Š°Š½Š½Ń тŠ° ŠæŠ¾ŃŠøŠ»Š°Š½Š½Ń +Copy\ citation\ key\ and\ title=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø ŠŗŠ»ŃŽŃ‡ цŠøтуŠ²Š°Š½Š½Ń тŠ° Š·Š°Š³Š¾Š»Š¾Š²Š¾Šŗ +Copy\ citation\ key\ with\ configured\ cite\ command=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø ŠŗŠ»ŃŽŃ‡ цŠøтуŠ²Š°Š½Š½Ń Š· Š½Š°Š»Š°ŃˆŃ‚Š¾Š²Š°Š½Š¾ŃŽ ŠŗŠ¾Š¼Š°Š½Š“Š¾ŃŽ цŠøтуŠ²Š°Š½Š½Ń +Copy\ to\ clipboard=ŠšŠ¾ŠæіюŠ²Š°Ń‚Šø Š² Š±ŃƒŃ„ŠµŃ€ Š¾Š±Š¼Ń–Š½Ńƒ +Could\ not\ call\ executable=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ Š²ŠøŠŗŠ»ŠøŠŗŠ°Ń‚Šø Š²ŠøŠŗŠ¾Š½Š°Š²Ń‡ŠøŠ¹ фŠ°Š¹Š» +Could\ not\ export\ preferences=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ ŠµŠŗсŠæŠ¾Ń€Ń‚ŃƒŠ²Š°Ń‚Šø Š½Š°Š»Š°ŃˆŃ‚ŃƒŠ²Š°Š½Š½Ń +Could\ not\ find\ a\ suitable\ import\ format.=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ Š·Š½Š°Š¹Ń‚Šø Š²Ń–Š“ŠæŠ¾Š²Ń–Š“Š½ŠøŠ¹ фŠ¾Ń€Š¼Š°Ń‚ іŠ¼ŠæŠ¾Ń€Ń‚Ńƒ. +Could\ not\ import\ preferences=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ іŠ¼ŠæŠ¾Ń€Ń‚ŃƒŠ²Š°Ń‚Šø Š½Š°Š»Š°ŃˆŃ‚ŃƒŠ²Š°Š½Š½Ń +Could\ not\ instantiate\ %0=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ стŠ²Š¾Ń€ŠøтŠø %0 +Could\ not\ instantiate\ %0\ %1=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ стŠ²Š¾Ń€ŠøтŠø %0 %1 +Could\ not\ instantiate\ %0.\ Have\ you\ chosen\ the\ correct\ package\ path?=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ стŠ²Š¾Ń€ŠøтŠø %0. Š’Šø Š¾Š±Ń€Š°Š»Šø ŠæрŠ°Š²ŠøŠ»ŃŒŠ½ŠøŠ¹ шŠ»ŃŃ… Š“Š¾ ŠæŠ°ŠŗŠµŃ‚Ńƒ? +Could\ not\ print\ preview=ŠŠµ Š²Š“Š°Š»Š¾ŃŃ Š½Š°Š“руŠŗуŠ²Š°Ń‚Šø ŠæŠ¾ŠæŠµŃ€ŠµŠ“Š½Ń–Š¹ ŠæŠµŃ€ŠµŠ³Š»ŃŠ“ +Create\ custom\ fields\ for\ each\ BibTeX\ entry=Š”тŠ²Š¾Ń€ŠøтŠø Š²Š»Š°ŃŠ½Ń– ŠæŠ¾Š»Ń Š“Š»Ń ŠŗŠ¾Š¶Š½Š¾Š³Š¾ Š·Š°ŠæŠøсу BibTeX +Current\ content\:\ %0=ŠŸŠ¾Ń‚Š¾Ń‡Š½ŠøŠ¹ Š²Š¼Ń–ст\: %0 +Current\ value\:\ %0=ŠŸŠ¾Ń‚Š¾Ń‡Š½Šµ Š·Š½Š°Ń‡ŠµŠ½Š½Ń\: %0 diff --git a/src/main/resources/l10n/JabRef_vi.properties b/src/main/resources/l10n/JabRef_vi.properties index 49979058375..7187fdcf00f 100644 --- a/src/main/resources/l10n/JabRef_vi.properties +++ b/src/main/resources/l10n/JabRef_vi.properties @@ -284,7 +284,6 @@ Field\ name=TĆŖn dį»Æ liį»‡u Field\ to\ group\ by=Dį»Æ liį»‡u gį»™p nhĆ³m theo Filter=Lį»c -Filter\ groups=NhĆ³m bį»™ lį»c Generate=Tįŗ”o diff --git a/src/main/resources/l10n/JabRef_zh_CN.properties b/src/main/resources/l10n/JabRef_zh_CN.properties index 6bd3ae6c20f..f2276cf4d21 100644 --- a/src/main/resources/l10n/JabRef_zh_CN.properties +++ b/src/main/resources/l10n/JabRef_zh_CN.properties @@ -267,7 +267,6 @@ Donate\ to\ JabRef=Donate Download\ file=äø‹č½½ę–‡ä»¶ -Downloaded\ website\ as\ an\ HTML\ file.=将ē½‘ē«™äø‹č½½äøŗHTMLꖇ件怂 duplicate\ removal=ē§»é™¤é‡å¤ @@ -348,7 +347,6 @@ Manage\ field\ names\ &\ content=ē®”ē†å­—ę®µåē§°å’Œå†…容 Field\ to\ group\ by=ē”Øę„åˆ†ē»„ēš„å­—ę®µ Filter=ē­›é€‰ -Filter\ groups=ē­›é€‰ē»„ Success\!\ Finished\ writing\ metadata.=Success\! Finished writing metadata. Error\ while\ writing\ metadata.\ See\ the\ error\ log\ for\ details.=å†™å…„å…ƒę•°ę®ę—¶å‡ŗ错怂čƦē»†äæ”ęÆčƷ参阅错čÆÆę—„åæ—怂 @@ -1812,7 +1810,6 @@ Could\ not\ copy\ file=ę— ę³•å¤åˆ¶ę–‡ä»¶ Copied\ %0\ files\ of\ %1\ successfully\ to\ %2=Copied %0 files of %1 successfully to %2 Rename\ failed=é‡å‘½åå¤±č“„ JabRef\ cannot\ access\ the\ file\ because\ it\ is\ being\ used\ by\ another\ process.=JabRef ę— ę³•č®æ问čÆ„ę–‡ä»¶, 因äøŗ另äø€äøŖčæ›ēØ‹ę­£åœØä½æē”Øå®ƒć€‚ -Show\ console\ output\ (only\ when\ the\ launcher\ is\ used)=Show console output (only when the launcher is used) Remove\ line\ breaks=ē§»é™¤ę¢č”Œē¬¦ Removes\ all\ line\ breaks\ in\ the\ field\ content.=ē§»é™¤å­—ę®µå†…å®¹äø­ēš„ę‰€ęœ‰ę¢č”Œē¬¦ć€‚ @@ -2320,7 +2317,6 @@ Convert\ timestamp\ field\ to\ field\ 'modificationdate'=将 timestamp å­—ę®µč½¬ New\ entry\ by\ type=ꌉē±»åž‹åˆ›å»ŗę–°ę”ē›® File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ '%0'=ꖇ件%1ę˜Æ%0ēš„é‡å¤ć€‚äæē•™%0 -File\ '%1'\ is\ a\ duplicate\ of\ '%0'.\ Keeping\ both\ due\ to\ deletion\ error=ꖇ件%1ę˜Æ%0ēš„é‡å¤ć€‚ē”±äŗŽåˆ é™¤é”™čÆÆåŒę—¶äæē•™äø¤č€…怂 Enable\ field\ formatters=åÆē”Øå­—ę®µę ¼å¼åŒ–å™Ø Entry\ Type=ę”ē›®ē±»åž‹ diff --git a/src/main/resources/l10n/JabRef_zh_TW.properties b/src/main/resources/l10n/JabRef_zh_TW.properties index e75a7a1d852..050baf57ba6 100644 --- a/src/main/resources/l10n/JabRef_zh_TW.properties +++ b/src/main/resources/l10n/JabRef_zh_TW.properties @@ -280,7 +280,6 @@ Field\ names\ are\ not\ allowed\ to\ contain\ white\ spaces\ or\ certain\ charac Filter=ēÆ©éø -Filter\ groups=ēÆ©éøē¾¤ēµ„ Generate=ē”Ÿęˆ From 3f88e6e9c7f6456bfeaa2cad4555d612fe0ac35f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:34:08 +0000 Subject: [PATCH 32/54] Bump org.apache.commons:commons-text from 1.11.0 to 1.12.0 (#11275) Bumps org.apache.commons:commons-text from 1.11.0 to 1.12.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-text dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 11ed7431454..9be7e392f5f 100644 --- a/build.gradle +++ b/build.gradle @@ -164,7 +164,7 @@ dependencies { implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.10.0' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0' - implementation group: 'org.apache.commons', name: 'commons-text', version: '1.11.0' + implementation group: 'org.apache.commons', name: 'commons-text', version: '1.12.0' implementation 'com.h2database:h2-mvstore:2.2.224' // required for reading write-protected PDFs - see https://github.com/JabRef/jabref/pull/942#issuecomment-209252635 From c2888d844bf1b474bffd5c0bc8769101753669cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:34:15 +0000 Subject: [PATCH 33/54] Bump jakarta.ws.rs:jakarta.ws.rs-api from 3.1.0 to 4.0.0 (#11276) Bumps jakarta.ws.rs:jakarta.ws.rs-api from 3.1.0 to 4.0.0. --- updated-dependencies: - dependency-name: jakarta.ws.rs:jakarta.ws.rs-api dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9be7e392f5f..8137ad0b950 100644 --- a/build.gradle +++ b/build.gradle @@ -268,7 +268,7 @@ dependencies { implementation group: 'org.jooq', name: 'jool', version: '0.9.15' // JAX-RS implemented by Jersey // API - implementation 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0' + implementation 'jakarta.ws.rs:jakarta.ws.rs-api:4.0.0' // Implementation of the API implementation 'org.glassfish.jersey.core:jersey-server:3.1.6' // injection framework From a9983a0573a2d7a3d1c512f352db37fd1221ee4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:34:35 +0000 Subject: [PATCH 34/54] Bump org.apache.commons:commons-csv from 1.10.0 to 1.11.0 (#11277) Bumps [org.apache.commons:commons-csv](https://github.com/apache/commons-csv) from 1.10.0 to 1.11.0. - [Changelog](https://github.com/apache/commons-csv/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-csv/compare/rel/commons-csv-1.10.0...rel/commons-csv-1.11.0) --- updated-dependencies: - dependency-name: org.apache.commons:commons-csv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8137ad0b950..c03acc2782a 100644 --- a/build.gradle +++ b/build.gradle @@ -162,7 +162,7 @@ dependencies { implementation "org.apache.lucene:lucene-analysis-common:$luceneVersion" implementation "org.apache.lucene:lucene-highlighter:$luceneVersion" - implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.10.0' + implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.11.0' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0' implementation group: 'org.apache.commons', name: 'commons-text', version: '1.12.0' implementation 'com.h2database:h2-mvstore:2.2.224' From 17ba16307a26586399d6d892293f2f366aafedd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:37:52 +0000 Subject: [PATCH 35/54] Bump com.dlsc.gemsfx:gemsfx from 2.10.0 to 2.12.0 (#11278) Bumps [com.dlsc.gemsfx:gemsfx](https://github.com/dlsc-software-consulting-gmbh/GemsFX) from 2.10.0 to 2.12.0. - [Release notes](https://github.com/dlsc-software-consulting-gmbh/GemsFX/releases) - [Changelog](https://github.com/dlsc-software-consulting-gmbh/GemsFX/blob/master/jreleaser.yml) - [Commits](https://github.com/dlsc-software-consulting-gmbh/GemsFX/compare/v2.10.0...v2.12.0) --- updated-dependencies: - dependency-name: com.dlsc.gemsfx:gemsfx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c03acc2782a..e001f630b52 100644 --- a/build.gradle +++ b/build.gradle @@ -218,7 +218,7 @@ dependencies { } implementation 'org.fxmisc.flowless:flowless:0.7.2' implementation 'org.fxmisc.richtext:richtextfx:0.11.2' - implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.10.0') { + implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.12.0') { exclude module: 'javax.inject' // Split package, use only jakarta.inject exclude module: 'commons-lang3' exclude group: 'org.openjfx' From 35089520d7f014268b85a854f68102c46064179b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:41:06 +0000 Subject: [PATCH 36/54] Bump io.github.classgraph:classgraph from 4.8.170 to 4.8.172 (#11279) Bumps [io.github.classgraph:classgraph](https://github.com/classgraph/classgraph) from 4.8.170 to 4.8.172. - [Release notes](https://github.com/classgraph/classgraph/releases) - [Commits](https://github.com/classgraph/classgraph/compare/classgraph-4.8.170...classgraph-4.8.172) --- updated-dependencies: - dependency-name: io.github.classgraph:classgraph dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e001f630b52..b3190ce21e2 100644 --- a/build.gradle +++ b/build.gradle @@ -298,7 +298,7 @@ dependencies { implementation 'commons-io:commons-io:2.16.1' - testImplementation 'io.github.classgraph:classgraph:4.8.170' + testImplementation 'io.github.classgraph:classgraph:4.8.172' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.2' From f346a448519c8a8a45d211cd24ea026e8756545c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:57:19 +0000 Subject: [PATCH 37/54] Bump src/main/resources/csl-styles from `32348cb` to `5338902` (#11280) Bumps [src/main/resources/csl-styles](https://github.com/citation-style-language/styles) from `32348cb` to `5338902`. - [Release notes](https://github.com/citation-style-language/styles/releases) - [Commits](https://github.com/citation-style-language/styles/compare/32348cb790b809081a0ff42ce81f6bcba050e4de...533890226e8abbbeb31cd63a5b1180ffc6e4870d) --- updated-dependencies: - dependency-name: src/main/resources/csl-styles dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index 32348cb790b..533890226e8 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit 32348cb790b809081a0ff42ce81f6bcba050e4de +Subproject commit 533890226e8abbbeb31cd63a5b1180ffc6e4870d From 858168922c8be2346fa5e1a79e0396d0983139ff Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 7 May 2024 02:24:42 +0200 Subject: [PATCH 38/54] Fix missing add opens for javafx on mac (#11273) * Fix missing add opens for javafx on mac fixes https://github.com/JabRef/jabref-issue-melting-pot/issues/428 * add also to org.jabref --- .github/workflows/deployment-arm64.yml | 17 +++++++++++++++-- .github/workflows/deployment.yml | 18 ++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index 01675246e2b..a7337738223 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -130,7 +130,14 @@ jobs: --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref.merged.module \ --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref.merged.module \ - --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module + --java-options --add-opens=javafx.controls/javafx.scene.control.skin=org.jabref.merged.module \ + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref \ + --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref - name: Build pkg (macOS) if: (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash @@ -160,7 +167,13 @@ jobs: --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref.merged.module \ --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref.merged.module \ - --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref \ + --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref - name: Rename files with arm64 suffix as well if: (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0fe694d2a30..030790397d1 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -142,7 +142,14 @@ jobs: --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref.merged.module \ --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref.merged.module \ - --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module + --java-options --add-opens=javafx.controls/javafx.scene.control.skin=org.jabref.merged.module \ + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref \ + --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref - name: Build pkg (macOS) if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash @@ -172,7 +179,14 @@ jobs: --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref.merged.module \ --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref.merged.module \ - --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module + --java-options --add-opens=javafx.controls/javafx.scene.control.skin=org.jabref.merged.module \ + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref.merged.module \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-exports=javafx.base/com.sun.javafx.event=org.jabref \ + --java-options --add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.graphics/javafx.scene=org.jabref \ + --java-options --add-opens=javafx.controls/javafx.scene.control=org.jabref \ + --java-options --add-opens=javafx.controls/com.sun.javafx.scene.control=org.jabref - name: Build runtime image and installer (linux, Windows) if: (matrix.os != 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') shell: bash From 045f8157a2916fe8b7b004ca67ae215cf0c106f0 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 7 May 2024 23:51:33 +0200 Subject: [PATCH 39/54] Fix jacoco plugin (#11283) --- build.gradle | 16 +++--- docs/code-howtos/testing.md | 97 +++++++++++++++++++++++-------------- 2 files changed, 70 insertions(+), 43 deletions(-) diff --git a/build.gradle b/build.gradle index b3190ce21e2..6466119ed31 100644 --- a/build.gradle +++ b/build.gradle @@ -575,16 +575,18 @@ databaseTest.mustRunAfter jacocoPrepare fetcherTest.mustRunAfter jacocoPrepare jacocoTestReport { + dependsOn jacocoPrepare, test, fetcherTest, databaseTest + executionData files( - "$buildDir/jacoco/test.exec", - "$buildDir/jacoco/databaseTest.exec", - "$buildDir/jacoco/fetcherTest.exec").filter { it.exists() } - dependsOn jacocoPrepare, test, databaseTest, fetcherTest + layout.buildDirectory.file('jacoco/test.exec').get().asFile, + layout.buildDirectory.file('jacoco/fetcherTest.exec').get().asFile, + layout.buildDirectory.file('jacoco/databaseTest.exec').get().asFile) reports { - csv.getRequired().set(true) - html.getRequired().set(true) - xml.getRequired().set(true) // coveralls plugin depends on xml format report + csv.required = true + html.required = true + // coveralls plugin depends on xml format report + xml.required = true } } diff --git a/docs/code-howtos/testing.md b/docs/code-howtos/testing.md index 88a9f6d3dbc..88f8ab84d12 100644 --- a/docs/code-howtos/testing.md +++ b/docs/code-howtos/testing.md @@ -3,20 +3,7 @@ parent: Code Howtos --- # Testing JabRef -## Background on Java testing - -In JabRef, we mainly rely on basic JUnit tests to increase code coverage. There are other ways to test: - -| Type | Techniques | Tool (Java) | Kind of tests | Used In JabRef | -| -------------- | ------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | -| Functional | Dynamics, black box, positive and negative | [JUnit-QuickCheck](https://github.com/pholser/junit-quickcheck) | Random data generation | No, not intended, because other test kinds seem more helpful. | -| Functional | Dynamics, black box, positive and negative | [GraphWalker](https://graphwalker.github.io) | Model-based | No, because the BibDatabase doesn't need to be tests | -| Functional | Dynamics, black box, positive and negative | [TestFX](https://github.com/TestFX/TestFX) | GUI Tests | Yes | -| Functional | Dynamics, white box, negative | [PIT](https://pitest.org) | Mutation | No | -| Functional | Dynamics, white box, positive and negative | [Mockito](https://site.mockito.org) | Mocking | Yes | -| Non-functional | Dynamics, black box, positive and negative | [JETM](http://jetm.void.fm), [Apache JMeter](https://jmeter.apache.org) | Performance (performance testing vs load testing respectively) | No | -| Structural | Static, white box | [CheckStyle](https://checkstyle.sourceforge.io) | Constient formatting of the source code | Yes | -| Structural | Dynamics, white box | [SpotBugs](https://spotbugs.github.io) | Reocurreing bugs (based on experience of other projects) | No | +In JabRef, we mainly rely on basic [JUnit](https://junit.org/junit5/docs/current/user-guide/) unit tests to increase code coverage. ## General hints on tests @@ -27,10 +14,10 @@ Imagine you want to test the method `format(String value)` in the class `BracesF * _Test only one thing per test:_ tests should be short and test only one small part of the method. So instead of ```java - testFormat() { - assertEqual("test", format("test")); - assertEqual("{test", format("{test")); - assertEqual("test", format("test}}")); + void format() { + assertEqual("test", format("test")); + assertEqual("{test", format("{test")); + assertEqual("test", format("test}}")); } ``` @@ -38,18 +25,42 @@ Imagine you want to test the method `format(String value)` in the class `BracesF * Do _not just test happy paths_, but also wrong/weird input. * It is recommended to write tests _before_ you actually implement the functionality (test driven development). * _Bug fixing:_ write a test case covering the bug and then fix it, leaving the test as a security that the bug will never reappear. -* Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, ()->doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method. +* Do not catch exceptions in tests, instead use the `assertThrows(Exception.class, () -> doSomethingThrowsEx())` feature of [junit-jupiter](https://junit.org/junit5/docs/current/user-guide/) to the test method. + +## Coverage + +IntelliJ has build in test coverage reports. Choose "Run with coverage". + +For a full coverage report as HTML, execute the gradle task `jacocoTestReport` (available in the "verification" folder in IntelliJ). +Then, you will find which shows the coverage of the tests. ## Lists in tests -* Use `assertEquals(Collections.emptyList(), actualList);` instead of `assertEquals(0, actualList.size());` to test whether a list is empty. -* Similarly, use `assertEquals(Arrays.asList("a", "b"), actualList);` to compare lists instead of +Instead of - ```java - assertEquals(2, actualList.size()); - assertEquals("a", actualList.get(0)); - assertEquals("b", actualList.get(1)); - ``` +```java +assertTrue(actualList.isEmpty()); +``` + +use + +```java +assertEquals(List.of(), actualList); +``` + +Similarly, to compare lists, instead of following code: + +```java +assertEquals(2, actualList.size()); +assertEquals("a", actualList.get(0)); +assertEquals("b", actualList.get(1)); +``` + +use the following code: + +```java +assertEquals(List.of("a", "b"), actualList); +``` ## BibEntries in tests @@ -57,19 +68,18 @@ Imagine you want to test the method `format(String value)` in the class `BracesF ## Files and folders in tests -* If you need a temporary file in tests, then add the following Annotation before the class: +If you need a temporary file in tests, use the `@TempDir` annotation: - ```java - @ExtendWith(TempDirectory.class) - class TestClass{ +```java +class TestClass{ - @BeforeEach - void setUp(@TempDirectory.TempDir Path temporaryFolder){ - } - } - ``` + @Test + void deletionWorks(@TempDir Path tempDir) { + } +} +``` - to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See the [junit-pioneer doc](https://junit-pioneer.org/docs/temp-directory/) for more details. +to the test class. A temporary file is now created by `Files.createFile(path)`. Using this pattern automatically ensures that the test folder is deleted after the tests are run. See for more details. ## Loading Files from Resources @@ -129,3 +139,18 @@ docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=jabref -p 3800:3307 mys ``` Set the environment variable `DBMS` to `mysql`. + +## Advanced testing and further reading + +On top of basic unit testing, there are more ways to test a software: + +| Type | Techniques | Tool (Java) | Kind of tests | Used In JabRef | +| -------------- | ------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | +| Functional | Dynamics, black box, positive and negative | [JUnit-QuickCheck](https://github.com/pholser/junit-quickcheck) | Random data generation | No, not intended, because other test kinds seem more helpful. | +| Functional | Dynamics, black box, positive and negative | [GraphWalker](https://graphwalker.github.io) | Model-based | No, because the BibDatabase doesn't need to be tests | +| Functional | Dynamics, black box, positive and negative | [TestFX](https://github.com/TestFX/TestFX) | GUI Tests | Yes | +| Functional | Dynamics, white box, negative | [PIT](https://pitest.org) | Mutation | No | +| Functional | Dynamics, white box, positive and negative | [Mockito](https://site.mockito.org) | Mocking | Yes | +| Non-functional | Dynamics, black box, positive and negative | [JETM](http://jetm.void.fm), [Apache JMeter](https://jmeter.apache.org) | Performance (performance testing vs load testing respectively) | No | +| Structural | Static, white box | [CheckStyle](https://checkstyle.sourceforge.io) | Constient formatting of the source code | Yes | +| Structural | Dynamics, white box | [SpotBugs](https://spotbugs.github.io) | Reocurreing bugs (based on experience of other projects) | No | From fb93c2f5958eac92c41ea3d64fe5b377a55ba0e6 Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 8 May 2024 22:36:20 +0200 Subject: [PATCH 40/54] Store preview divider pos in entry editor (#11285) * Store preview divider pos in entry editor Fixes https://github.com/JabRef/jabref/issues/11281 * checkstyle --- CHANGELOG.md | 1 + .../entryeditor/EntryEditorPreferences.java | 20 ++++++++++++++++++- .../gui/entryeditor/FieldsEditorTab.java | 15 ++++++++++++++ .../jabref/preferences/JabRefPreferences.java | 10 ++++++++-- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 410b5341a73..b972f15d2cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We fixed an issue where drag and dropping entries created a shallow copy. [#11160](https://github.com/JabRef/jabref/issues/11160) - We fixed an issue where imports to a custom group would only work for the first entry [#11085](https://github.com/JabRef/jabref/issues/11085), [#11269](https://github.com/JabRef/jabref/issues/11269) - We fixed an issue where a new entry was not added to the selected group [#8933](https://github.com/JabRef/jabref/issues/8933) +- We fixed an issue where the horizontal position of the Entry Preview inside the entry editor was not remembered across restarts [#11281](https://github.com/JabRef/jabref/issues/11281) ### Removed diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java index ae0fda0194c..fcca2b100ad 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java @@ -49,6 +49,7 @@ public static JournalPopupEnabled fromString(String status) { private final ObjectProperty enablementStatus; private final BooleanProperty shouldShowSciteTab; private final BooleanProperty showUserCommentsFields; + private final DoubleProperty previewWidthDividerPosition; public EntryEditorPreferences(Map> entryEditorTabList, Map> defaultEntryEditorTabList, @@ -62,7 +63,8 @@ public EntryEditorPreferences(Map> entryEditorTabList, boolean autolinkFilesEnabled, JournalPopupEnabled journalPopupEnabled, boolean showSciteTab, - boolean showUserCommentsFields) { + boolean showUserCommentsFields, + double previewWidthDividerPosition) { this.entryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(entryEditorTabList)); this.defaultEntryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(defaultEntryEditorTabList)); @@ -77,6 +79,7 @@ public EntryEditorPreferences(Map> entryEditorTabList, this.enablementStatus = new SimpleObjectProperty<>(journalPopupEnabled); this.shouldShowSciteTab = new SimpleBooleanProperty(showSciteTab); this.showUserCommentsFields = new SimpleBooleanProperty(showUserCommentsFields); + this.previewWidthDividerPosition = new SimpleDoubleProperty(previewWidthDividerPosition); } public ObservableMap> getEntryEditorTabs() { @@ -226,4 +229,19 @@ public BooleanProperty showUserCommentsFieldsProperty() { public void setShowUserCommentsFields(boolean showUserCommentsFields) { this.showUserCommentsFields.set(showUserCommentsFields); } + + public void setPreviewWidthDividerPosition(double previewWidthDividerPosition) { + this.previewWidthDividerPosition.set(previewWidthDividerPosition); + } + + /** + * Holds the horizontal divider position when the Preview is shown in the entry editor + */ + public DoubleProperty previewWidthDividerPositionProperty() { + return previewWidthDividerPosition; + } + + public Double getPreviewWidthDividerPosition() { + return previewWidthDividerPosition.get(); + } } diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 20e54396bfe..97348f5838a 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -41,6 +41,7 @@ import org.jabref.preferences.PreferencesService; import com.tobiasdiez.easybind.EasyBind; +import com.tobiasdiez.easybind.Subscription; /** * A single tab displayed in the EntryEditor holding several FieldEditors. @@ -61,6 +62,7 @@ abstract class FieldsEditorTab extends EntryEditorTab { private PreviewPanel previewPanel; private final UndoManager undoManager; private Collection fields = new ArrayList<>(); + private Subscription dividerPositionSubscription; public FieldsEditorTab(boolean compressed, BibDatabaseContext databaseContext, @@ -258,9 +260,22 @@ private void initPanel() { container.getItems().remove(previewPanel); } else { container.getItems().add(1, previewPanel); + container.setDividerPositions(preferences.getEntryEditorPreferences().getPreviewWidthDividerPosition()); } }); + + // save position + dividerPositionSubscription = EasyBind.valueAt(container.getDividers(), 0) + .mapObservable(SplitPane.Divider::positionProperty) + .subscribeToValues(this::savePreviewWidthDividerPosition); setContent(container); } } + + private void savePreviewWidthDividerPosition(Number position) { + if (!preferences.getPreviewPreferences().shouldShowPreviewAsExtraTab()) { + preferences.getEntryEditorPreferences().setPreviewWidthDividerPosition(position.doubleValue()); + } + } } + diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index d9c747b3a1e..59b01569a34 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -173,6 +173,10 @@ public class JabRefPreferences implements PreferencesService { public static final String BIBLATEX_DEFAULT_MODE = "biblatexMode"; public static final String NAMES_AS_IS = "namesAsIs"; public static final String ENTRY_EDITOR_HEIGHT = "entryEditorHeightFX"; + /** + * Holds the horizontal divider position of the preview view when it is shown inside the entry editor + */ + public static final String ENTRY_EDITOR_PREVIEW_DIVIDER_POS = "entryEditorPreviewDividerPos"; public static final String AUTO_RESIZE_MODE = "autoResizeMode"; public static final String WINDOW_MAXIMISED = "windowMaximised"; public static final String WINDOW_FULLSCREEN = "windowFullscreen"; @@ -602,6 +606,7 @@ private JabRefPreferences() { defaults.put(WINDOW_FULLSCREEN, Boolean.FALSE); defaults.put(AUTO_RESIZE_MODE, Boolean.FALSE); // By default disable "Fit table horizontally on the screen" defaults.put(ENTRY_EDITOR_HEIGHT, 0.65); + defaults.put(ENTRY_EDITOR_PREVIEW_DIVIDER_POS, 0.5); defaults.put(NAMES_AS_IS, Boolean.FALSE); // "Show names unchanged" defaults.put(NAMES_FIRST_LAST, Boolean.FALSE); // "Show 'Firstname Lastname'" defaults.put(NAMES_NATBIB, Boolean.TRUE); // "Natbib style" @@ -1488,7 +1493,8 @@ public EntryEditorPreferences getEntryEditorPreferences() { getBoolean(AUTOLINK_FILES_ENABLED), EntryEditorPreferences.JournalPopupEnabled.fromString(get(JOURNAL_POPUP)), getBoolean(SHOW_SCITE_TAB), - getBoolean(SHOW_USER_COMMENTS_FIELDS)); + getBoolean(SHOW_USER_COMMENTS_FIELDS), + getDouble(ENTRY_EDITOR_PREVIEW_DIVIDER_POS)); EasyBind.listen(entryEditorPreferences.entryEditorTabs(), (obs, oldValue, newValue) -> storeEntryEditorTabs(newValue)); // defaultEntryEditorTabs are read-only @@ -1503,7 +1509,7 @@ public EntryEditorPreferences getEntryEditorPreferences() { EasyBind.listen(entryEditorPreferences.enableJournalPopupProperty(), (obs, oldValue, newValue) -> put(JOURNAL_POPUP, newValue.toString())); EasyBind.listen(entryEditorPreferences.shouldShowLSciteTabProperty(), (obs, oldValue, newValue) -> putBoolean(SHOW_SCITE_TAB, newValue)); EasyBind.listen(entryEditorPreferences.showUserCommentsFieldsProperty(), (obs, oldValue, newValue) -> putBoolean(SHOW_USER_COMMENTS_FIELDS, newValue)); - + EasyBind.listen(entryEditorPreferences.previewWidthDividerPositionProperty(), (obs, oldValue, newValue) -> putDouble(ENTRY_EDITOR_PREVIEW_DIVIDER_POS, newValue.doubleValue())); return entryEditorPreferences; } From 71efec0e1e222e4944b79680b7232c7ed4825dd3 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sun, 12 May 2024 12:57:42 +0200 Subject: [PATCH 41/54] Try parallel tests (#9797) * Try parallel tests * Try to have "normal tests" running with 1 fork only * Fix typo --- build.gradle | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 6466119ed31..5095d6e76b4 100644 --- a/build.gradle +++ b/build.gradle @@ -525,16 +525,32 @@ testlogger { showStackTraces false } +tasks.withType(Test) { + reports.html.outputLocation.set(file("${reporting.baseDir}/${name}")) + // Enable parallel tests. See https://docs.gradle.org/8.1/userguide/performance.html#execute_tests_in_parallel for details. + maxParallelForks = Runtime.runtime.availableProcessors() - 1 +} + tasks.register('databaseTest', Test) { useJUnitPlatform { includeTags 'DatabaseTest' } + + testLogging { + // set options for log level LIFECYCLE + events = ["FAILED"] + exceptionFormat "full" + } + + maxParallelForks = 1 } tasks.register('fetcherTest', Test) { useJUnitPlatform { includeTags 'FetcherTest' } + + maxParallelForks = 1 } tasks.register('guiTest', Test) { @@ -547,6 +563,8 @@ tasks.register('guiTest', Test) { events = ["FAILED"] exceptionFormat "full" } + + maxParallelForks = 1 } // Test result tasks @@ -556,10 +574,6 @@ tasks.register('copyTestResources', Copy) { } processTestResources.dependsOn copyTestResources -tasks.withType(Test) { - reports.html.outputLocation.set(file("${reporting.baseDir}/${name}")) -} - tasks.register('jacocoPrepare') { doFirst { // Ignore failures of tests From 0eab5dcdf3c6ce37944e38025a11c83749517ba5 Mon Sep 17 00:00:00 2001 From: Rohit Garga <134286153+rohit-garga@users.noreply.github.com> Date: Mon, 13 May 2024 14:30:18 +0530 Subject: [PATCH 42/54] Introduce formatter to remove word-enclosing braces (#11253) * #11222 fix * checkstyle fixes * fixes for unit tests * Make new RemoveWordEnclosingAndOuterEnclosingBracesFormatter available * Add more braces to the example input * Keep original string when formatting empty string * Fix RemoveWordEnclosingAndOuterEnclosingBracesFormatter in the case of }} at the end * Fix constant (and empty line) * Reorder methods * Add CHANGELOG.md entry * markdown issue fix * checkstyle fix * duplicate keys issue fix * author class not being initialized fix * reverting key name change for old formatter * reverting commit- Keep original string when formatting empty string since it causes unit tests to fail * minor fixes * minor fixes * minor fixes * fix: rewrite * formatting fix --------- Co-authored-by: Oliver Kopp --- CHANGELOG.md | 1 + .../jabref/logic/formatter/Formatters.java | 6 +- ...va => RemoveEnclosingBracesFormatter.java} | 22 +-- ...osingAndOuterEnclosingBracesFormatter.java | 110 +++++++++++++++ .../fetcher/AstrophysicsDataSystem.java | 6 +- .../logic/importer/fetcher/CrossRef.java | 4 +- .../importer/fetcher/INSPIREFetcher.java | 4 +- .../jabref/logic/importer/fetcher/ZbMATH.java | 6 +- .../jabref/logic/msbib/MSBibConverter.java | 4 +- .../org/jabref/logic/msbib/MsBibAuthor.java | 4 +- .../style/OOBibStyleGetCitationMarker.java | 4 +- .../java/org/jabref/model/entry/Author.java | 125 +++++------------- src/main/resources/l10n/JabRef_en.properties | 2 + ...> RemoveEnclosingBracesFormatterTest.java} | 4 +- ...gAndOuterEnclosingBracesFormatterTest.java | 31 +++++ 15 files changed, 208 insertions(+), 125 deletions(-) rename src/main/java/org/jabref/logic/formatter/bibtexfields/{RemoveBracesFormatter.java => RemoveEnclosingBracesFormatter.java} (97%) create mode 100644 src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatter.java rename src/test/java/org/jabref/logic/formatter/bibtexfields/{RemoveBracesFormatterTest.java => RemoveEnclosingBracesFormatterTest.java} (90%) create mode 100644 src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatterTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b972f15d2cc..a2cc933da15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added value selection (such as for month) for content selectors in custom entry types. [#11109](https://github.com/JabRef/jabref/issues/11109) - We added a duplicate checker for the Citation Relations tab. [#10414](https://github.com/JabRef/jabref/issues/10414) - We added tooltip on main table cells that shows cell content or cell content and entry preview if set in preferences. [10925](https://github.com/JabRef/jabref/issues/10925) +- Added a formatter to remove word enclosing braces. [#11222](https://github.com/JabRef/jabref/issues/11222) - We added the ability to add a keyword/crossref when typing the separator character (e.g., comma) in the keywords/crossref fields. [#11178](https://github.com/JabRef/jabref/issues/11178) - We added an exporter and improved the importer for Endnote XML format. [#11137](https://github.com/JabRef/jabref/issues/11137) - We added support for automatically update LaTeX citations when a LaTeX file is created, removed, or modified. [#10585](https://github.com/JabRef/jabref/issues/10585) diff --git a/src/main/java/org/jabref/logic/formatter/Formatters.java b/src/main/java/org/jabref/logic/formatter/Formatters.java index bcc7c202295..fb827f9ab8e 100644 --- a/src/main/java/org/jabref/logic/formatter/Formatters.java +++ b/src/main/java/org/jabref/logic/formatter/Formatters.java @@ -25,7 +25,8 @@ import org.jabref.logic.formatter.bibtexfields.NormalizeUnicodeFormatter; import org.jabref.logic.formatter.bibtexfields.OrdinalsToSuperscriptFormatter; import org.jabref.logic.formatter.bibtexfields.RegexFormatter; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveWordEnclosingAndOuterEnclosingBracesFormatter; import org.jabref.logic.formatter.bibtexfields.ShortenDOIFormatter; import org.jabref.logic.formatter.bibtexfields.UnicodeToLatexFormatter; import org.jabref.logic.formatter.bibtexfields.UnitsToLatexFormatter; @@ -82,7 +83,8 @@ public static List getOthers() { new NormalizeNamesFormatter(), new NormalizePagesFormatter(), new OrdinalsToSuperscriptFormatter(), - new RemoveBracesFormatter(), + new RemoveEnclosingBracesFormatter(), + new RemoveWordEnclosingAndOuterEnclosingBracesFormatter(), new UnitsToLatexFormatter(), new EscapeUnderscoresFormatter(), new EscapeAmpersandsFormatter(), diff --git a/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatter.java b/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveEnclosingBracesFormatter.java similarity index 97% rename from src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatter.java rename to src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveEnclosingBracesFormatter.java index c29b081cc48..31b071aac12 100644 --- a/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatter.java +++ b/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveEnclosingBracesFormatter.java @@ -6,7 +6,7 @@ import org.jspecify.annotations.NullMarked; @NullMarked -public class RemoveBracesFormatter extends Formatter { +public class RemoveEnclosingBracesFormatter extends Formatter { @Override public String getName() { @@ -18,6 +18,16 @@ public String getKey() { return "remove_braces"; } + @Override + public String getDescription() { + return Localization.lang("Removes braces encapsulating the complete field content."); + } + + @Override + public String getExampleInput() { + return "{In CDMA}"; + } + @Override public String format(String value) { String formatted = value; @@ -37,16 +47,6 @@ public String format(String value) { return formatted; } - @Override - public String getDescription() { - return Localization.lang("Removes braces encapsulating the complete field content."); - } - - @Override - public String getExampleInput() { - return "{In CDMA}"; - } - /** * Check if a string at any point has had more ending } braces than opening { ones. * Will e.g. return true for the string "DNA} text {EPA" diff --git a/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatter.java b/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatter.java new file mode 100644 index 00000000000..c70c7315ebe --- /dev/null +++ b/src/main/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatter.java @@ -0,0 +1,110 @@ +package org.jabref.logic.formatter.bibtexfields; + +import java.util.Objects; +import java.util.StringJoiner; + +import org.jabref.logic.cleanup.Formatter; +import org.jabref.logic.l10n.Localization; +import org.jabref.model.strings.StringUtil; + +import org.jspecify.annotations.NullMarked; + +/** + * Removes start and end brace both at the complete string and at beginning/end of a word + *

+ * E.g., + *

    + *
  • {Vall{\'e}e Poussin} -> Vall{\'e}e Poussin
  • + *
  • {Vall{\'e}e} {Poussin} -> Vall{\'e}e Poussin
  • + *
  • Vall{\'e}e Poussin -> Vall{\'e}e Poussin
  • + *
+ */ +@NullMarked +public class RemoveWordEnclosingAndOuterEnclosingBracesFormatter extends Formatter { + + private static final RemoveEnclosingBracesFormatter REMOVE_ENCLOSING_BRACES_FORMATTER = new RemoveEnclosingBracesFormatter(); + + @Override + public String getName() { + return Localization.lang("Remove word enclosing braces"); + } + + @Override + public String getKey() { + return "remove_enclosing_and_outer_enclosing_braces"; + } + + @Override + public String getDescription() { + return Localization.lang("Removes braces encapsulating a complete word and the complete field content."); + } + + @Override + public String getExampleInput() { + return "{In {CDMA}}"; + } + + @Override + public String format(String input) { + Objects.requireNonNull(input); + if (StringUtil.isBlank(input)) { + return input; + } + + if (!input.contains("{")) { + return input; + } + + // We need to first remove the outer braces to have double braces at the last word working (e.g., {In {CDMA}}) + input = REMOVE_ENCLOSING_BRACES_FORMATTER.format(input); + + String[] split = input.split(" "); + StringJoiner result = new StringJoiner(" "); + for (String s : split) { + if ((s.length() > 2) && s.startsWith("{") && s.endsWith("}")) { + // quick solution (which we don't do): just remove first "{" and last "}" + // however, it might be that s is like {A}bbb{c}, where braces may not be removed + + String inner = s.substring(1, s.length() - 1); + + if (inner.contains("}")) { + if (properBrackets(inner)) { + s = inner; + } + } else { + // no inner curly brackets found, no check needed, inner can just be used as s + s = inner; + } + } + result.add(s); + } + return result.toString(); + } + + /** + * @return true iff the brackets in s are properly paired + */ + private boolean properBrackets(String s) { + // nested construct is there, check for "proper" nesting + int i = 0; + int level = 0; + while (i < s.length()) { + char c = s.charAt(i); + switch (c) { + case '{': + level++; + break; + case '}': + level--; + if (level == -1) { // improper nesting + return false; + } + break; + default: + break; + } + i++; + } + return level == 0; + } +} diff --git a/src/main/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystem.java b/src/main/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystem.java index 7b85d1534cd..23e5c077d8b 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystem.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/AstrophysicsDataSystem.java @@ -17,7 +17,7 @@ import org.jabref.logic.formatter.bibtexfields.ClearFormatter; import org.jabref.logic.formatter.bibtexfields.NormalizeMonthFormatter; import org.jabref.logic.formatter.bibtexfields.NormalizeNamesFormatter; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; import org.jabref.logic.formatter.bibtexfields.RemoveNewlinesFormatter; import org.jabref.logic.help.HelpFile; import org.jabref.logic.importer.EntryBasedParserFetcher; @@ -149,9 +149,9 @@ public Parser getParser() { @Override public void doPostCleanup(BibEntry entry) { - new FieldFormatterCleanup(StandardField.ABSTRACT, new RemoveBracesFormatter()).cleanup(entry); + new FieldFormatterCleanup(StandardField.ABSTRACT, new RemoveEnclosingBracesFormatter()).cleanup(entry); new FieldFormatterCleanup(StandardField.ABSTRACT, new RemoveNewlinesFormatter()).cleanup(entry); - new FieldFormatterCleanup(StandardField.TITLE, new RemoveBracesFormatter()).cleanup(entry); + new FieldFormatterCleanup(StandardField.TITLE, new RemoveEnclosingBracesFormatter()).cleanup(entry); new FieldFormatterCleanup(StandardField.AUTHOR, new NormalizeNamesFormatter()).cleanup(entry); new FieldFormatterCleanup(StandardField.MONTH, new NormalizeMonthFormatter()).cleanup(entry); diff --git a/src/main/java/org/jabref/logic/importer/fetcher/CrossRef.java b/src/main/java/org/jabref/logic/importer/fetcher/CrossRef.java index 4826239e3e6..c34277add1e 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/CrossRef.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/CrossRef.java @@ -11,7 +11,7 @@ import org.jabref.logic.cleanup.FieldFormatterCleanup; import org.jabref.logic.formatter.bibtexfields.ClearFormatter; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; import org.jabref.logic.importer.EntryBasedParserFetcher; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.IdBasedParserFetcher; @@ -46,7 +46,7 @@ public class CrossRef implements IdParserFetcher, EntryBasedParserFetcher, private static final String API_URL = "https://api.crossref.org/works"; - private static final RemoveBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveBracesFormatter(); + private static final RemoveEnclosingBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveEnclosingBracesFormatter(); @Override public String getName() { diff --git a/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java b/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java index 67f9e84afc0..86d27594a84 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/INSPIREFetcher.java @@ -11,7 +11,7 @@ import org.jabref.logic.cleanup.FieldFormatterCleanup; import org.jabref.logic.formatter.bibtexfields.ClearFormatter; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; import org.jabref.logic.help.HelpFile; import org.jabref.logic.importer.EntryBasedFetcher; import org.jabref.logic.importer.FetcherException; @@ -76,7 +76,7 @@ public void doPostCleanup(BibEntry entry) { new FieldFormatterCleanup(new UnknownField("SLACcitation"), new ClearFormatter()).cleanup(entry); // Remove braces around content of "title" field - new FieldFormatterCleanup(StandardField.TITLE, new RemoveBracesFormatter()).cleanup(entry); + new FieldFormatterCleanup(StandardField.TITLE, new RemoveEnclosingBracesFormatter()).cleanup(entry); new FieldFormatterCleanup(StandardField.TITLE, new LatexToUnicodeFormatter()).cleanup(entry); } 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 70afa7847c1..c8398a1e8f3 100644 --- a/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java +++ b/src/main/java/org/jabref/logic/importer/fetcher/ZbMATH.java @@ -9,7 +9,7 @@ import org.jabref.logic.cleanup.FieldFormatterCleanup; import org.jabref.logic.cleanup.MoveFieldCleanup; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; import org.jabref.logic.importer.EntryBasedParserFetcher; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.IdBasedParserFetcher; @@ -131,7 +131,7 @@ public Parser getParser() { public void doPostCleanup(BibEntry entry) { new MoveFieldCleanup(new UnknownField("msc2010"), StandardField.KEYWORDS).cleanup(entry); new MoveFieldCleanup(AMSField.FJOURNAL, StandardField.JOURNAL).cleanup(entry); - new FieldFormatterCleanup(StandardField.JOURNAL, new RemoveBracesFormatter()).cleanup(entry); - new FieldFormatterCleanup(StandardField.TITLE, new RemoveBracesFormatter()).cleanup(entry); + new FieldFormatterCleanup(StandardField.JOURNAL, new RemoveEnclosingBracesFormatter()).cleanup(entry); + new FieldFormatterCleanup(StandardField.TITLE, new RemoveEnclosingBracesFormatter()).cleanup(entry); } } diff --git a/src/main/java/org/jabref/logic/msbib/MSBibConverter.java b/src/main/java/org/jabref/logic/msbib/MSBibConverter.java index d565195e6fc..1870254e933 100644 --- a/src/main/java/org/jabref/logic/msbib/MSBibConverter.java +++ b/src/main/java/org/jabref/logic/msbib/MSBibConverter.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Optional; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; import org.jabref.model.entry.AuthorList; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.Month; @@ -18,7 +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 static final RemoveEnclosingBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveEnclosingBracesFormatter(); private MSBibConverter() { } diff --git a/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java b/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java index bcceef9657f..9285a4d9d05 100644 --- a/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java +++ b/src/main/java/org/jabref/logic/msbib/MsBibAuthor.java @@ -1,11 +1,11 @@ package org.jabref.logic.msbib; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; import org.jabref.model.entry.Author; public class MsBibAuthor { - private static final RemoveBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveBracesFormatter(); + private static final RemoveEnclosingBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveEnclosingBracesFormatter(); private String firstName; private String middleName; 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 c38e3d37ed0..aff4c1ce84c 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.Optional; -import org.jabref.logic.formatter.bibtexfields.RemoveBracesFormatter; +import org.jabref.logic.formatter.bibtexfields.RemoveEnclosingBracesFormatter; import org.jabref.model.database.BibDatabase; import org.jabref.model.entry.Author; import org.jabref.model.entry.AuthorList; @@ -24,7 +24,7 @@ class OOBibStyleGetCitationMarker { - private static final RemoveBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveBracesFormatter(); + private static final RemoveEnclosingBracesFormatter REMOVE_BRACES_FORMATTER = new RemoveEnclosingBracesFormatter(); private OOBibStyleGetCitationMarker() { } diff --git a/src/main/java/org/jabref/model/entry/Author.java b/src/main/java/org/jabref/model/entry/Author.java index e4511e7ef07..5892ca0b457 100644 --- a/src/main/java/org/jabref/model/entry/Author.java +++ b/src/main/java/org/jabref/model/entry/Author.java @@ -3,6 +3,8 @@ import java.util.Objects; import java.util.Optional; +import org.jabref.architecture.AllowedToUseLogic; +import org.jabref.logic.formatter.bibtexfields.RemoveWordEnclosingAndOuterEnclosingBracesFormatter; import org.jabref.model.strings.LatexToUnicodeAdapter; import org.jabref.model.strings.StringUtil; @@ -11,6 +13,7 @@ *

* Current usage: only methods getLastOnly, getFirstLast, and getLastFirst are used; all other methods are provided for completeness. */ +@AllowedToUseLogic("because it needs to use formatter") public class Author { /** @@ -20,6 +23,8 @@ public class Author { */ public static final Author OTHERS = new Author("", "", null, "others", null); + public static final RemoveWordEnclosingAndOuterEnclosingBracesFormatter FORMATTER = new RemoveWordEnclosingAndOuterEnclosingBracesFormatter(); + private final String givenName; private final String givenNameAbbreviated; private final String namePrefix; @@ -41,17 +46,37 @@ public class Author { 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 (!StringUtil.isBlank(givenName)) { + this.givenName = addDotIfAbbreviation(FORMATTER.format(givenName)); + } else { + this.givenName = null; + } + if (!StringUtil.isBlank(givenNameAbbreviated)) { + this.givenNameAbbreviated = FORMATTER.format(givenNameAbbreviated); + } else { + this.givenNameAbbreviated = null; + } + if (!StringUtil.isBlank(namePrefix)) { + this.namePrefix = FORMATTER.format(namePrefix); + } else { + this.namePrefix = null; + } 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); + if (!StringUtil.isBlank(familyName)) { + this.familyName = FORMATTER.format(familyName); + } else { + this.familyName = null; + } + } + if (!StringUtil.isBlank(nameSuffix)) { + this.nameSuffix = FORMATTER.format(nameSuffix); + } else { + this.nameSuffix = null; } - this.nameSuffix = removeStartAndEndBraces(nameSuffix); } public static String addDotIfAbbreviation(String name) { @@ -165,94 +190,6 @@ public boolean equals(Object other) { return false; } - /** - * @return true iff the brackets in s are properly paired - */ - private boolean properBrackets(String s) { - // nested construct is there, check for "proper" nesting - int i = 0; - int level = 0; - while (i < s.length()) { - char c = s.charAt(i); - switch (c) { - case '{': - level++; - break; - case '}': - level--; - if (level == -1) { // improper nesting - return false; - } - break; - default: - break; - } - i++; - } - return level == 0; - } - - /** - * Removes start and end brace both at the complete string and at beginning/end of a word - *

- * E.g., - *

    - *
  • {Vall{\'e}e Poussin} -> Vall{\'e}e Poussin
  • - *
  • {Vall{\'e}e} {Poussin} -> Vall{\'e}e Poussin
  • - *
  • Vall{\'e}e Poussin -> Vall{\'e}e Poussin
  • - *
- */ - private String removeStartAndEndBraces(String name) { - if (StringUtil.isBlank(name)) { - return null; - } - - if (!name.contains("{")) { - return name; - } - - String[] split = name.split(" "); - StringBuilder b = new StringBuilder(); - for (String s : split) { - if ((s.length() > 2) && s.startsWith("{") && s.endsWith("}")) { - // quick solution (which we don't do: just remove first "{" and last "}" - // however, it might be that s is like {A}bbb{c}, where braces may not be removed - - // inner - String inner = s.substring(1, s.length() - 1); - - if (inner.contains("}")) { - if (properBrackets(inner)) { - s = inner; - } - } else { - // no inner curly brackets found, no check needed, inner can just be used as s - s = inner; - } - } - b.append(s).append(' '); - } - // delete last - b.deleteCharAt(b.length() - 1); - - // now, all inner words are cleared - // case {word word word} remains - // as above, we have to be aware of {w}ord word wor{d} and {{w}ord word word} - - String newName = b.toString(); - - if (newName.startsWith("{") && newName.endsWith("}")) { - String inner = newName.substring(1, newName.length() - 1); - if (properBrackets(inner)) { - return inner; - } else { - return newName; - } - } else { - return newName; - } - } - /** * Returns the first name of the author stored in this object ("First"). * @@ -304,7 +241,7 @@ public Optional getNameSuffix() { * @return 'von Last' */ public String getNamePrefixAndFamilyName() { - if (namePrefix == null) { + if (namePrefix == null || "".equals(namePrefix)) { return getFamilyName().orElse(""); } else { return familyName == null ? namePrefix : namePrefix + ' ' + familyName; diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index b8326bbbe12..21c49a26efb 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1480,6 +1480,8 @@ Protect\ terms=Protect terms Add\ enclosing\ braces=Add enclosing braces Add\ braces\ encapsulating\ the\ complete\ field\ content.=Add braces encapsulating the complete field content. Remove\ enclosing\ braces=Remove enclosing braces +Remove\ word\ enclosing\ braces=Remove word enclosing braces +Removes\ braces\ encapsulating\ a\ complete\ word\ and\ the\ complete\ field\ content.=Removes braces encapsulating a complete word and the complete field content. Removes\ braces\ encapsulating\ the\ complete\ field\ content.=Removes braces encapsulating the complete field content. Removes\ all\ balanced\ {}\ braces\ around\ words.=Removes all balanced {} braces around words. Shorten\ DOI=Shorten DOI diff --git a/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatterTest.java b/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveEnclosingBracesFormatterTest.java similarity index 90% rename from src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatterTest.java rename to src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveEnclosingBracesFormatterTest.java index 37eff160e67..45f6e87373d 100644 --- a/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveBracesFormatterTest.java +++ b/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveEnclosingBracesFormatterTest.java @@ -9,9 +9,9 @@ /** * Tests in addition to the general tests from {@link org.jabref.logic.formatter.FormatterTest} */ -public class RemoveBracesFormatterTest { +public class RemoveEnclosingBracesFormatterTest { - private final RemoveBracesFormatter formatter = new RemoveBracesFormatter(); + private final RemoveEnclosingBracesFormatter formatter = new RemoveEnclosingBracesFormatter(); @ParameterizedTest @CsvSource({ diff --git a/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatterTest.java b/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatterTest.java new file mode 100644 index 00000000000..036268c7929 --- /dev/null +++ b/src/test/java/org/jabref/logic/formatter/bibtexfields/RemoveWordEnclosingAndOuterEnclosingBracesFormatterTest.java @@ -0,0 +1,31 @@ +package org.jabref.logic.formatter.bibtexfields; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RemoveWordEnclosingAndOuterEnclosingBracesFormatterTest { + private final RemoveWordEnclosingAndOuterEnclosingBracesFormatter formatter = new RemoveWordEnclosingAndOuterEnclosingBracesFormatter(); + + @ParameterizedTest + @CsvSource({ + "A test B, {A} test {B}", + "A and B, {{A} and {B}}", + "{w}ord word wor{d}, {w}ord word wor{d}", + "{w}ord word word, {{w}ord word word}", + "{w}ord word wor{d}, {w}ord word {wor{d}}", + "Vall{\\'e}e Poussin, {Vall{\\'e}e} {Poussin}", + "Vall{\\'e}e Poussin, {Vall{\\'e}e Poussin}", + "Vall{\\'e}e Poussin, Vall{\\'e}e Poussin" + }) + public void format(String expected, String input) { + assertEquals(expected, formatter.format(input)); + } + + @Test + public void formatExample() { + assertEquals("In CDMA", formatter.format(formatter.getExampleInput())); + } +} From efc60e8b83f84aa29996ee47ecd51e8078dc1987 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:32:36 +0000 Subject: [PATCH 43/54] Bump org.openrewrite.recipe:rewrite-recipe-bom from 2.9.0 to 2.11.0 (#11288) Bumps [org.openrewrite.recipe:rewrite-recipe-bom](https://github.com/openrewrite/rewrite-recipe-bom) from 2.9.0 to 2.11.0. - [Release notes](https://github.com/openrewrite/rewrite-recipe-bom/releases) - [Commits](https://github.com/openrewrite/rewrite-recipe-bom/compare/v2.9.0...v2.11.0) --- updated-dependencies: - dependency-name: org.openrewrite.recipe:rewrite-recipe-bom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5095d6e76b4..2c481dbe9e9 100644 --- a/build.gradle +++ b/build.gradle @@ -316,7 +316,7 @@ dependencies { xjc group: 'org.glassfish.jaxb', name: 'jaxb-xjc', version: '3.0.2' xjc group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '3.0.2' - rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.9.0")) + rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:2.11.0")) rewrite("org.openrewrite.recipe:rewrite-static-analysis") rewrite("org.openrewrite.recipe:rewrite-logging-frameworks") rewrite("org.openrewrite.recipe:rewrite-testing-frameworks") From ebee05b743348ce36b76efa1a087f161ad66adb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:32:44 +0000 Subject: [PATCH 44/54] Bump com.dlsc.gemsfx:gemsfx from 2.12.0 to 2.16.0 (#11287) Bumps [com.dlsc.gemsfx:gemsfx](https://github.com/dlsc-software-consulting-gmbh/GemsFX) from 2.12.0 to 2.16.0. - [Release notes](https://github.com/dlsc-software-consulting-gmbh/GemsFX/releases) - [Changelog](https://github.com/dlsc-software-consulting-gmbh/GemsFX/blob/master/jreleaser.yml) - [Commits](https://github.com/dlsc-software-consulting-gmbh/GemsFX/compare/v2.12.0...v2.16.0) --- updated-dependencies: - dependency-name: com.dlsc.gemsfx:gemsfx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2c481dbe9e9..269f26fa278 100644 --- a/build.gradle +++ b/build.gradle @@ -218,7 +218,7 @@ dependencies { } implementation 'org.fxmisc.flowless:flowless:0.7.2' implementation 'org.fxmisc.richtext:richtextfx:0.11.2' - implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.12.0') { + implementation (group: 'com.dlsc.gemsfx', name: 'gemsfx', version: '2.16.0') { exclude module: 'javax.inject' // Split package, use only jakarta.inject exclude module: 'commons-lang3' exclude group: 'org.openjfx' From 7aacd76931155db83a1774d6988027390bccc8b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:34:25 +0000 Subject: [PATCH 45/54] Bump com.fasterxml.jackson.datatype:jackson-datatype-jsr310 (#11289) Bumps com.fasterxml.jackson.datatype:jackson-datatype-jsr310 from 2.17.0 to 2.17.1. --- updated-dependencies: - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 269f26fa278..93de7feab05 100644 --- a/build.gradle +++ b/build.gradle @@ -185,7 +185,7 @@ dependencies { implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.9.0.202403050737-r' implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.17.0' - implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.17.0' + implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.17.1' implementation 'com.fasterxml:aalto-xml:1.3.2' From 5077e9a8e6f239fc697718472a29e75a6f13bc9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:40:54 +0000 Subject: [PATCH 46/54] Bump org.mockito:mockito-core from 5.11.0 to 5.12.0 (#11291) Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.11.0 to 5.12.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.11.0...v5.12.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 93de7feab05..7db0adec82c 100644 --- a/build.gradle +++ b/build.gradle @@ -302,7 +302,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.junit.platform:junit-platform-launcher:1.10.2' - testImplementation 'org.mockito:mockito-core:5.11.0' + testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.xmlunit:xmlunit-core:2.10.0' testImplementation 'org.xmlunit:xmlunit-matchers:2.10.0' testRuntimeOnly 'com.tngtech.archunit:archunit-junit5-engine:1.3.0' From fa13c6ecb6f3f5758d8918a6301a9abfe1e59382 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:49:22 +0000 Subject: [PATCH 47/54] Bump src/main/resources/csl-styles from `5338902` to `434df0a` (#11292) Bumps [src/main/resources/csl-styles](https://github.com/citation-style-language/styles) from `5338902` to `434df0a`. - [Release notes](https://github.com/citation-style-language/styles/releases) - [Commits](https://github.com/citation-style-language/styles/compare/533890226e8abbbeb31cd63a5b1180ffc6e4870d...434df0ad75d7416bb651ad1f08826a5152c77a27) --- updated-dependencies: - dependency-name: src/main/resources/csl-styles dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/main/resources/csl-styles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles index 533890226e8..434df0ad75d 160000 --- a/src/main/resources/csl-styles +++ b/src/main/resources/csl-styles @@ -1 +1 @@ -Subproject commit 533890226e8abbbeb31cd63a5b1180ffc6e4870d +Subproject commit 434df0ad75d7416bb651ad1f08826a5152c77a27 From 708b4c7ca6c4a528c67282cfcf5bfd981f1bd27b Mon Sep 17 00:00:00 2001 From: Loay Ghreeb <52158423+LoayGhreeb@users.noreply.github.com> Date: Mon, 13 May 2024 21:50:11 +0300 Subject: [PATCH 48/54] Remove outdated pdf indexed files from Lucene index (#11293) --- .../org/jabref/logic/pdf/search/PdfIndexer.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java b/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java index 7b66b1c62ca..be3d5e8faf5 100644 --- a/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java +++ b/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java @@ -205,7 +205,7 @@ public void addToIndex(BibEntry entry, Collection linkedFiles, boole private void doCommit() { try { - getIndexWriter().ifPresent(Unchecked.consumer(writer -> writer.commit())); + getIndexWriter().ifPresent(Unchecked.consumer(IndexWriter::commit)); } catch (UncheckedIOException e) { LOGGER.warn("Could not commit changes to the index.", e); } @@ -274,7 +274,6 @@ private void addToIndex(BibEntry entry, LinkedFile linkedFile, boolean shouldCom LOGGER.debug("Could not find {}", linkedFile.getLink()); return; } - LOGGER.debug("Adding {} to index", linkedFile.getLink()); try { // Check if a document with this path is already in the index try { @@ -283,17 +282,21 @@ private void addToIndex(BibEntry entry, LinkedFile linkedFile, boolean shouldCom TopDocs topDocs = searcher.search(query, 1); // If a document was found, check if is less current than the one in the FS if (topDocs.scoreDocs.length > 0) { - Document doc = reader.document(topDocs.scoreDocs[0].doc); + Document doc = reader.storedFields().document(topDocs.scoreDocs[0].doc); long indexModificationTime = Long.parseLong(doc.getField(SearchFieldConstants.MODIFIED).stringValue()); BasicFileAttributes attributes = Files.readAttributes(resolvedPath.get(), BasicFileAttributes.class); if (indexModificationTime >= attributes.lastModifiedTime().to(TimeUnit.SECONDS)) { - LOGGER.debug("File {} is already indexed", linkedFile.getLink()); + LOGGER.debug("File {} is already indexed and up-to-date.", linkedFile.getLink()); return; + } else { + LOGGER.debug("File {} is already indexed but outdated. Removing from index.", linkedFile.getLink()); + removeFromIndex(linkedFile.getLink()); } } } catch (IndexNotFoundException e) { LOGGER.debug("Index not found. Continuing.", e); } + LOGGER.debug("Adding {} to index", linkedFile.getLink()); // If no document was found, add the new one Optional> pages = new DocumentReader(entry, filePreferences).readLinkedPdf(this.databaseContext, linkedFile); if (pages.isPresent()) { @@ -328,7 +331,7 @@ public Set getListOfFilePaths() { MatchAllDocsQuery query = new MatchAllDocsQuery(); TopDocs allDocs = searcher.search(query, Integer.MAX_VALUE); for (ScoreDoc scoreDoc : allDocs.scoreDocs) { - Document doc = reader.document(scoreDoc.doc); + Document doc = reader.storedFields().document(scoreDoc.doc); paths.add(doc.getField(SearchFieldConstants.PATH).stringValue()); } } catch (IOException e) { From 6376067faf44a7e24a5fabed03856586b17fe501 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 09:13:20 +0200 Subject: [PATCH 49/54] Bump com.fasterxml.jackson.dataformat:jackson-dataformat-yaml (#11290) Bumps [com.fasterxml.jackson.dataformat:jackson-dataformat-yaml](https://github.com/FasterXML/jackson-dataformats-text) from 2.17.0 to 2.17.1. - [Commits](https://github.com/FasterXML/jackson-dataformats-text/compare/jackson-dataformats-text-2.17.0...jackson-dataformats-text-2.17.1) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Christoph --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7db0adec82c..f01129eab7b 100644 --- a/build.gradle +++ b/build.gradle @@ -184,7 +184,7 @@ dependencies { implementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '6.9.0.202403050737-r' - implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.17.0' + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.17.1' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.17.1' implementation 'com.fasterxml:aalto-xml:1.3.2' From 2d179422ee7c55b01a02218e50d131d025ad62aa Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Tue, 14 May 2024 16:05:56 +0200 Subject: [PATCH 50/54] Remove obsolete step (#11295) --- ...guidelines-intellij-open-module-settings.png | Bin 68332 -> 0 bytes .../intellij-12-build.md | 6 ------ 2 files changed, 6 deletions(-) delete mode 100644 docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/guidelines-intellij-open-module-settings.png diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/guidelines-intellij-open-module-settings.png b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/guidelines-intellij-open-module-settings.png deleted file mode 100644 index deef5f86734870d8d929b44ec96116d2abecbedc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68332 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≤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 OfRyA^Ws9LE0sjTgItqvY diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md index 1dfb4f01f58..3ffe0b5ca9c 100644 --- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md +++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md @@ -32,12 +32,6 @@ After clicking "Download", IntelliJ installs Eclipse Temurin: ![IntelliJ installs Eclipse Temurin](guidelines-intellij-installs-temurin.png) {% endfigure %} -Open the module settings: Right click on "JabRef" and select "Open Module Settings": - -{% figure caption:"Open IntelliJ Module Settings" %} -![IntelliJ Module Settings](guidelines-intellij-open-module-settings.png) -{% endfigure %} - Navigate to **Project Settings > Project** and ensure that the projects' SDK is Java 21. {% figure caption:"Project SDK is pinned to the downloaded SDK (showing JDK 21 as example)" %} From 3e1dbccfd557c02f39c499ad08aa15b2551f5b7b Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 17 May 2024 17:29:08 +0200 Subject: [PATCH 51/54] Try gradle build speedup (#11300) --- build.gradle | 3 +++ gradle.properties | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/build.gradle b/build.gradle index f01129eab7b..a545093ff9e 100644 --- a/build.gradle +++ b/build.gradle @@ -424,6 +424,9 @@ tasks.register('generateCitaviSource', XjcTask) { tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' + + // hint by https://docs.gradle.org/current/userguide/performance.html#run_the_compiler_as_a_separate_process + options.fork = true } compileJava { diff --git a/gradle.properties b/gradle.properties index 8e7f633fecd..7de2be111f2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,12 @@ org.gradle.vs.watch=true + +# hint by https://docs.gradle.org/current/userguide/performance.html#increase_the_heap_size org.gradle.jvmargs=-Xmx4096M + +# hint by https://docs.gradle.org/current/userguide/performance.html#enable_configuration_cache +# Does not work: +# - Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: cannot serialize object of type 'org.gradle.api.internal.project.DefaultProject', a subtype of 'org.gradle.api.Project', as these are not supported with the configuration cache. +# org.gradle.configuration-cache=false + +# hint by https://docs.gradle.org/current/userguide/performance.html#enable_the_build_cache +org.gradle.caching=true From 9fdc2946a1d23e089db4b691105e5311dd33372c Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 17 May 2024 17:29:49 +0200 Subject: [PATCH 52/54] Fix comment (#11299) --- src/main/java/org/jabref/model/entry/BibEntry.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index 6f463422a36..b52b827579f 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -45,6 +45,7 @@ import org.jabref.model.strings.StringUtil; import org.jabref.model.util.MultiKeyMap; +import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.EventBus; import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.optional.OptionalBinding; @@ -353,19 +354,22 @@ private Optional genericGetResolvedFieldOrAlias(Field field, BibDatabase } /** - * Returns this entry's ID. + * Returns this entry's ID. It is used internally to distinguish different BibTeX entries. + *

+ * It is not the citation key (which is stored in the {@link InternalField#KEY_FIELD} and also known as BibTeX key). */ public String getId() { return id; } /** - * Sets this entry's identifier (ID). It is used internally to distinguish different BibTeX entries. It is not the citation key. The BibTexKey is the {@link InternalField#KEY_FIELD}. + * Sets this entry's identifier (ID). *

* The entry is also updated in the shared database - provided the database containing it doesn't veto the change. * * @param id The ID to be used */ + @VisibleForTesting public void setId(String id) { Objects.requireNonNull(id, "Every BibEntry must have an ID"); From b12f65c1de48572efd68cb4e8b2416e16cb61366 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb <52158423+LoayGhreeb@users.noreply.github.com> Date: Fri, 17 May 2024 23:45:20 +0300 Subject: [PATCH 53/54] Remove EnglishStemAnalyzer and use EnglishAnalyzer (#11301) --- .../jabref/logic/pdf/search/PdfIndexer.java | 4 +-- .../jabref/logic/pdf/search/PdfSearcher.java | 7 +++--- .../model/pdf/search/EnglishStemAnalyzer.java | 25 ------------------- .../jabref/model/pdf/search/SearchResult.java | 10 +++++--- 4 files changed, 13 insertions(+), 33 deletions(-) delete mode 100644 src/main/java/org/jabref/model/pdf/search/EnglishStemAnalyzer.java diff --git a/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java b/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java index be3d5e8faf5..45750ac5b84 100644 --- a/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java +++ b/src/main/java/org/jabref/logic/pdf/search/PdfIndexer.java @@ -16,11 +16,11 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.pdf.search.EnglishStemAnalyzer; import org.jabref.model.pdf.search.SearchFieldConstants; import org.jabref.preferences.FilePreferences; import com.google.common.annotations.VisibleForTesting; +import org.apache.lucene.analysis.en.EnglishAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexNotFoundException; @@ -130,7 +130,7 @@ private void initializeIndexWriterAndReader(IndexWriterConfig.OpenMode mode) { indexWriter = new IndexWriter( indexDirectory, new IndexWriterConfig( - new EnglishStemAnalyzer()).setOpenMode(mode)); + new EnglishAnalyzer()).setOpenMode(mode)); } catch (IOException e) { LOGGER.error("Could not initialize the IndexWriter", e); // FIXME: This can also happen if another instance of JabRef is launched in parallel. diff --git a/src/main/java/org/jabref/logic/pdf/search/PdfSearcher.java b/src/main/java/org/jabref/logic/pdf/search/PdfSearcher.java index 40acc97f8af..fb6afccc29b 100644 --- a/src/main/java/org/jabref/logic/pdf/search/PdfSearcher.java +++ b/src/main/java/org/jabref/logic/pdf/search/PdfSearcher.java @@ -7,11 +7,12 @@ import java.util.Optional; import org.jabref.gui.LibraryTab; -import org.jabref.model.pdf.search.EnglishStemAnalyzer; import org.jabref.model.pdf.search.PdfSearchResults; import org.jabref.model.pdf.search.SearchResult; import org.jabref.model.strings.StringUtil; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.en.EnglishAnalyzer; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; @@ -31,7 +32,7 @@ public final class PdfSearcher { private static final Logger LOGGER = LoggerFactory.getLogger(LibraryTab.class); private final PdfIndexer indexer; - private EnglishStemAnalyzer englishStemAnalyzer = new EnglishStemAnalyzer(); + private final Analyzer englishAnalyzer = new EnglishAnalyzer(); private PdfSearcher(PdfIndexer indexer) { this.indexer = indexer; @@ -65,7 +66,7 @@ public PdfSearchResults search(final String searchString, final int maxHits) thr return new PdfSearchResults(); } try (IndexReader reader = DirectoryReader.open(optionalIndexWriter.get())) { - Query query = new MultiFieldQueryParser(PDF_FIELDS, englishStemAnalyzer).parse(searchString); + Query query = new MultiFieldQueryParser(PDF_FIELDS, englishAnalyzer).parse(searchString); IndexSearcher searcher = new IndexSearcher(reader); TopDocs results = searcher.search(query, maxHits); for (ScoreDoc scoreDoc : results.scoreDocs) { diff --git a/src/main/java/org/jabref/model/pdf/search/EnglishStemAnalyzer.java b/src/main/java/org/jabref/model/pdf/search/EnglishStemAnalyzer.java deleted file mode 100644 index 1dcccbb6583..00000000000 --- a/src/main/java/org/jabref/model/pdf/search/EnglishStemAnalyzer.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.jabref.model.pdf.search; - -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.LowerCaseFilter; -import org.apache.lucene.analysis.StopFilter; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.core.DecimalDigitFilter; -import org.apache.lucene.analysis.en.EnglishAnalyzer; -import org.apache.lucene.analysis.en.PorterStemFilter; -import org.apache.lucene.analysis.standard.StandardTokenizer; - -public class EnglishStemAnalyzer extends Analyzer { - - @Override - protected TokenStreamComponents createComponents(String fieldName) { - Tokenizer source = new StandardTokenizer(); - TokenStream filter = new LowerCaseFilter(source); - filter = new StopFilter(filter, EnglishAnalyzer.ENGLISH_STOP_WORDS_SET); - filter = new DecimalDigitFilter(filter); - filter = new PorterStemFilter(filter); - return new TokenStreamComponents(source, filter); - } -} - diff --git a/src/main/java/org/jabref/model/pdf/search/SearchResult.java b/src/main/java/org/jabref/model/pdf/search/SearchResult.java index 7a79191e94c..aeb70ff6779 100644 --- a/src/main/java/org/jabref/model/pdf/search/SearchResult.java +++ b/src/main/java/org/jabref/model/pdf/search/SearchResult.java @@ -7,7 +7,9 @@ import org.jabref.model.entry.BibEntry; +import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.en.EnglishAnalyzer; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; @@ -46,14 +48,16 @@ public SearchResult(IndexSearcher searcher, Query query, ScoreDoc scoreDoc) thro Highlighter highlighter = new Highlighter(new SimpleHTMLFormatter("", ""), new QueryScorer(query)); - try (TokenStream contentStream = new EnglishStemAnalyzer().tokenStream(CONTENT, content)) { + try (Analyzer analyzer = new EnglishAnalyzer(); + TokenStream contentStream = analyzer.tokenStream(CONTENT, content)) { TextFragment[] frags = highlighter.getBestTextFragments(contentStream, content, true, 10); this.contentResultStringsHtml = Arrays.stream(frags).map(TextFragment::toString).collect(Collectors.toList()); } catch (InvalidTokenOffsetsException e) { this.contentResultStringsHtml = List.of(); } - try (TokenStream annotationStream = new EnglishStemAnalyzer().tokenStream(ANNOTATIONS, annotations)) { + try (Analyzer analyzer = new EnglishAnalyzer(); + TokenStream annotationStream = analyzer.tokenStream(ANNOTATIONS, annotations)) { TextFragment[] frags = highlighter.getBestTextFragments(annotationStream, annotations, true, 10); this.annotationsResultStringsHtml = Arrays.stream(frags).map(TextFragment::toString).collect(Collectors.toList()); } catch (InvalidTokenOffsetsException e) { @@ -62,7 +66,7 @@ public SearchResult(IndexSearcher searcher, Query query, ScoreDoc scoreDoc) thro } private String getFieldContents(IndexSearcher searcher, ScoreDoc scoreDoc, String field) throws IOException { - IndexableField indexableField = searcher.doc(scoreDoc.doc).getField(field); + IndexableField indexableField = searcher.storedFields().document(scoreDoc.doc).getField(field); if (indexableField == null) { return ""; } From dfe1b0ce0bbd185ed5747b70e7ca24e4f05ebf37 Mon Sep 17 00:00:00 2001 From: Loay Ghreeb <52158423+LoayGhreeb@users.noreply.github.com> Date: Sat, 18 May 2024 21:47:55 +0300 Subject: [PATCH 54/54] Update latex citations status in JavaFx thread (#11302) * Update status in JavaFx thread * Update LatexCitationsTabViewModel.java --- .../org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java index 2549e9ec211..7f6d74cc94f 100644 --- a/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java +++ b/src/main/java/org/jabref/gui/entryeditor/LatexCitationsTabViewModel.java @@ -92,7 +92,7 @@ private FileAlterationListener getListener() { @Override public void onStart(FileAlterationObserver observer) { if (!updateStatusOnCreate.get()) { - status.set(Status.IN_PROGRESS); + DefaultTaskExecutor.runInJavaFXThread(() -> status.set(Status.IN_PROGRESS)); } }