Skip to content

Commit fe03fc4

Browse files
authored
Merge branch 'main' into fix-for-issue-12727
2 parents b38bed2 + a29091b commit fe03fc4

34 files changed

+783
-150
lines changed

.github/workflows/pr-format.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
name: Check PR Format
22

33
on:
4-
pull_request:
4+
# _target to run even in the case of merge conflicts
5+
pull_request_target:
56
types: [opened, reopened, edited]
67

78
permissions:

.github/workflows/tests-code.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,14 @@ jobs:
213213
214214
# exit 1 in case of an error
215215
remaining=$(
216-
grep -E " error " heylogx.txt \
216+
grep -E " error " heylogs.txt \
217217
| wc -l
218218
)
219219
220220
echo "Remaining errors: $remaining"
221221
222-
# We have on acceptable error
223-
if [ "$remaining" -gt 1 ]; then
222+
# We have two acceptable errors
223+
if [ "$remaining" -gt 2 ]; then
224224
echo "Failing because of $remaining remaining error(s)."
225225
exit 1
226226
fi

.jbang/JabSrvLauncher.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//DEPS info.picocli:picocli:4.7.7
2323
//DEPS org.postgresql:postgresql:42.7.8
2424
//DEPS org.bouncycastle:bcprov-jdk18on:1.83
25-
//DEPS com.konghq:unirest-modules-gson:4.6.0
25+
//DEPS com.konghq:unirest-modules-gson:4.7.0
2626
//DEPS jakarta.ws.rs:jakarta.ws.rs-api:4.0.0
2727
//DEPS org.glassfish.jersey.core:jersey-server:4.0.0
2828
//DEPS org.glassfish.jersey.inject:jersey-hk2:4.0.0
@@ -34,7 +34,7 @@
3434
//DEPS org.glassfish.grizzly:grizzly-framework:4.0.2
3535
//DEPS jakarta.validation:jakarta.validation-api:3.1.1
3636
//DEPS org.hibernate.validator:hibernate-validator:9.1.0.Final
37-
//DEPS com.konghq:unirest-modules-gson:4.6.0
37+
//DEPS com.konghq:unirest-modules-gson:4.7.0
3838
//DEPS com.google.guava:guava:33.5.0-jre
3939
//DEPS org.jabref:afterburner.fx:2.0.0
4040
//DEPS net.harawata:appdirs:1.5.0

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
3535
### Changed
3636

3737
- We replaced the standard ComboBox with a SearchableComboBox and added a free text field in custom Entry Types [#14082](https://github.com/JabRef/jabref/issues/14082)
38+
- In case of invalid BibTeX in the source tab, a notification is displayed (instead of an exception). [#14504](https://github.com/JabRef/jabref/pull/14504)
3839
- We separated the "Clean up entries" dialog into three tabs for clarity [#13819](https://github.com/JabRef/jabref/issues/13819)
3940
- `JabKit`: `--porcelain` does not output any logs to the console anymore. [#14244](https://github.com/JabRef/jabref/pull/14244)
4041
- <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd> now opens the terminal in the active library directory. [#14130](https://github.com/JabRef/jabref/issues/14130)

jabgui/src/main/java/org/jabref/gui/entryeditor/SourceTab.java

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public class SourceTab extends EntryEditorTab {
7070
private static final String SEARCH_STYLE = "search";
7171
private final FieldPreferences fieldPreferences;
7272
private final UndoManager undoManager;
73-
private final ObjectProperty<ValidationMessage> sourceIsValid = new SimpleObjectProperty<>();
73+
private final ObjectProperty<ValidationMessage> validationMessage = new SimpleObjectProperty<>();
7474
private final ObservableRuleBasedValidator sourceValidator = new ObservableRuleBasedValidator();
7575
private final ImportFormatPreferences importFormatPreferences;
7676
private final FileUpdateMonitor fileMonitor;
@@ -126,18 +126,20 @@ private void highlightSearchPattern() {
126126

127127
SearchQuery searchQuery = new SearchQuery(stateManager.searchQueryProperty().get());
128128
Map<Optional<Field>, List<String>> searchTermsMap = Highlighter.groupTermsByField(searchQuery);
129-
searchTermsMap.forEach((optionalField, terms) -> {
130-
Optional<String> searchPattern = Highlighter.buildSearchPattern(terms);
131-
if (searchPattern.isEmpty()) {
132-
return;
133-
}
129+
searchTermsMap.forEach(this::buildPatternAndHighlightField);
130+
}
134131

135-
if (optionalField.isPresent()) {
136-
highlightField(optionalField.get(), searchPattern.get());
137-
} else {
138-
fieldPositions.keySet().forEach(field -> highlightField(field, searchPattern.get()));
139-
}
140-
});
132+
private void buildPatternAndHighlightField(Optional<Field> optionalField, List<String> terms) {
133+
Highlighter.buildSearchPattern(terms).ifPresent(
134+
searchPattern -> {
135+
if (optionalField.isPresent()) {
136+
highlightField(optionalField.get(), searchPattern);
137+
} else {
138+
// User did not specify any field, thus we need to go through all fields
139+
fieldPositions.keySet().forEach(field -> highlightField(field, searchPattern));
140+
}
141+
}
142+
);
141143
}
142144

143145
private void highlightField(Field field, String searchPattern) {
@@ -155,21 +157,19 @@ private void highlightField(Field field, String searchPattern) {
155157
}
156158
}
157159

160+
/// Method similar to [BibEntry#getStringRepresentation(BibEntry, BibDatabaseMode, BibEntryTypesManager, FieldPreferences)]. This method additionally updates [#fieldPositions].
158161
private String getSourceString(BibEntry entry, BibDatabaseMode type, FieldPreferences fieldPreferences) throws IOException {
159-
StringWriter writer = new StringWriter();
160-
BibWriter bibWriter = new BibWriter(writer, "\n"); // JavaFX works with LF only
161-
FieldWriter fieldWriter = FieldWriter.buildIgnoreHashes(fieldPreferences);
162-
BibEntryWriter bibEntryWriter = new BibEntryWriter(fieldWriter, entryTypesManager);
163-
bibEntryWriter.write(entry, bibWriter, type, true);
164-
fieldPositions = bibEntryWriter.getFieldPositions();
165-
String sourceString = writer.toString();
166-
writer.close();
167-
return sourceString;
162+
try (StringWriter writer = new StringWriter()) {
163+
BibWriter bibWriter = new BibWriter(writer, "\n"); // JavaFX works with LF only
164+
FieldWriter fieldWriter = FieldWriter.buildIgnoreHashes(fieldPreferences);
165+
BibEntryWriter bibEntryWriter = new BibEntryWriter(fieldWriter, entryTypesManager);
166+
bibEntryWriter.write(entry, bibWriter, type, true);
167+
fieldPositions = bibEntryWriter.getFieldPositions();
168+
return writer.toString();
169+
}
168170
}
169171

170-
/* Work around for different input methods.
171-
* https://github.com/FXMisc/RichTextFX/issues/146
172-
*/
172+
/// Work around for different input methods. <https://github.com/FXMisc/RichTextFX/issues/146>
173173
private static class InputMethodRequestsObject implements InputMethodRequests {
174174

175175
@Override
@@ -218,7 +218,7 @@ private void setupSourceEditor() {
218218
contextMenu.getStyleClass().add("context-menu");
219219
codeArea.setContextMenu(contextMenu);
220220

221-
sourceValidator.addRule(sourceIsValid);
221+
sourceValidator.addRule(validationMessage);
222222

223223
sourceValidator.getValidationStatus().getMessages().addListener((InvalidationListener) c -> {
224224
ValidationStatus sourceValidationStatus = sourceValidator.getValidationStatus();
@@ -232,7 +232,7 @@ private void setupSourceEditor() {
232232
}
233233
});
234234

235-
codeArea.focusedProperty().addListener((obs, oldValue, onFocus) -> {
235+
codeArea.focusedProperty().addListener((_, _, onFocus) -> {
236236
if (!onFocus && (currentEntry != null)) {
237237
storeSource(currentEntry, codeArea.textProperty().getValue());
238238
}
@@ -293,32 +293,41 @@ private void storeSource(BibEntry outOfFocusEntry, String text) {
293293
BibDatabase database = parserResult.getDatabase();
294294

295295
if (database.getEntryCount() > 1) {
296-
throw new IllegalStateException("More than one entry found.");
296+
LOGGER.error("More than one entry found.");
297+
// We use the error dialog as the notification is hidden
298+
dialogService.showErrorDialogAndWait(Localization.lang("More than one entry found."));
299+
return;
297300
}
298301

299302
if (!database.hasEntries()) {
300303
if (parserResult.hasWarnings()) {
301-
// put the warning into as exception text -> it will be displayed to the user
302-
throw new IllegalStateException(parserResult.warnings().getFirst());
304+
LOGGER.warn("Could not store entry", parserResult.warnings());
305+
String errors = parserResult.getErrorMessage();
306+
dialogService.showErrorDialogAndWait(errors);
307+
validationMessage.setValue(ValidationMessage.error(Localization.lang("Failed to parse Bib(La)TeX: %0", errors)));
308+
return;
303309
} else {
304-
throw new IllegalStateException("No entries found.");
310+
LOGGER.warn("No entries found.");
311+
String errors = Localization.lang("No entries available");
312+
dialogService.showErrorDialogAndWait(errors);
313+
validationMessage.setValue(ValidationMessage.error(Localization.lang("Failed to parse Bib(La)TeX: %0", errors)));
314+
return;
305315
}
306316
}
307317

308318
if (parserResult.hasWarnings()) {
309-
// put the warning into as exception text -> it will be displayed to the user
310-
throw new IllegalStateException(parserResult.getErrorMessage());
319+
LOGGER.warn("Failed to parse Bib(La)TeX", parserResult.warnings());
320+
String errors = parserResult.getErrorMessage();
321+
dialogService.showErrorDialogAndWait(errors);
322+
validationMessage.setValue(ValidationMessage.error(Localization.lang("Failed to parse Bib(La)TeX: %0", errors)));
311323
}
312324

313325
NamedCompoundEdit compound = new NamedCompoundEdit(Localization.lang("source edit"));
314326
BibEntry newEntry = database.getEntries().getFirst();
315-
String newKey = newEntry.getCitationKey().orElse(null);
316-
317-
if (newKey != null) {
318-
outOfFocusEntry.setCitationKey(newKey);
319-
} else {
320-
outOfFocusEntry.clearCiteKey();
321-
}
327+
newEntry.getCitationKey()
328+
.ifPresentOrElse(
329+
outOfFocusEntry::setCitationKey,
330+
() -> outOfFocusEntry.clearCiteKey());
322331

323332
// First, remove fields that the user has removed.
324333
for (Map.Entry<Field, String> field : outOfFocusEntry.getFieldMap().entrySet()) {
@@ -353,9 +362,9 @@ private void storeSource(BibEntry outOfFocusEntry, String text) {
353362
compound.end();
354363
undoManager.addEdit(compound);
355364

356-
sourceIsValid.setValue(null);
357-
} catch (InvalidFieldValueException | IllegalStateException | IOException ex) {
358-
sourceIsValid.setValue(ValidationMessage.error(Localization.lang("Problem with parsing entry") + ": " + ex.getMessage()));
365+
validationMessage.setValue(null);
366+
} catch (InvalidFieldValueException | IOException ex) {
367+
validationMessage.setValue(ValidationMessage.error(Localization.lang("Failed to parse Bib(La)TeX: %0", ex.getMessage())));
359368
LOGGER.debug("Incorrect source", ex);
360369
}
361370
}

jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogPreferences.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@
77

88
import org.jabref.logic.externalfiles.DateRange;
99
import org.jabref.logic.externalfiles.ExternalFileSorter;
10+
import org.jabref.logic.util.StandardFileType;
1011

1112
public class UnlinkedFilesDialogPreferences {
1213
private final StringProperty unlinkedFilesSelectedExtension;
1314
private final ObjectProperty<DateRange> unlinkedFilesSelectedDateRange;
1415
private final ObjectProperty<ExternalFileSorter> unlinkedFilesSelectedSort;
1516

17+
private UnlinkedFilesDialogPreferences() {
18+
this(
19+
StandardFileType.ANY_FILE.getName(),
20+
DateRange.ALL_TIME,
21+
ExternalFileSorter.DEFAULT
22+
);
23+
}
24+
1625
public UnlinkedFilesDialogPreferences(String unlinkedFilesSelectedExtension,
1726
DateRange unlinkedFilesSelectedDateRange,
1827
ExternalFileSorter unlinkedFilesSelectedSort) {
@@ -21,6 +30,16 @@ public UnlinkedFilesDialogPreferences(String unlinkedFilesSelectedExtension,
2130
this.unlinkedFilesSelectedSort = new SimpleObjectProperty<>(unlinkedFilesSelectedSort);
2231
}
2332

33+
public static UnlinkedFilesDialogPreferences getDefault() {
34+
return new UnlinkedFilesDialogPreferences();
35+
}
36+
37+
public void setAll(UnlinkedFilesDialogPreferences preferences) {
38+
this.unlinkedFilesSelectedExtension.set(preferences.getUnlinkedFilesSelectedExtension());
39+
this.unlinkedFilesSelectedDateRange.set(preferences.getUnlinkedFilesSelectedDateRange());
40+
this.unlinkedFilesSelectedSort.set(preferences.getUnlinkedFilesSelectedSort());
41+
}
42+
2443
public String getUnlinkedFilesSelectedExtension() {
2544
return unlinkedFilesSelectedExtension.get();
2645
}

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryView.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ private void initializeSpecifyBibTeX() {
357357
bibtexText.textProperty().bindBidirectional(viewModel.bibtexTextProperty());
358358
final String clipboardText = ClipBoardManager.getContents().trim();
359359
if (!StringUtil.isBlank(clipboardText)) {
360-
// :TODO: Better validation would be nice here, so clipboard text is only copied over if it matches a
361-
// supported Bib(La)Tex source format.
360+
// TODO: Better validation would be nice here, so clipboard text is only copied over if it matches a
361+
// supported Bib(La)TeX source format.
362362
bibtexText.setText(clipboardText);
363363
bibtexText.selectAll();
364364
}

jabgui/src/main/java/org/jabref/gui/newentry/NewEntryViewModel.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ public void executeSpecifyBibtex() {
460460
final Throwable exception = interpretWorker.getException();
461461
final String exceptionMessage = exception.getMessage();
462462

463-
final String dialogTitle = Localization.lang("Failed to parse Bib(La)Tex");
463+
final String dialogTitle = Localization.lang("Failed to parse Bib(La)TeX");
464464

465465
if (exception instanceof ParseException) {
466466
dialogService.showInformationDialogAndWait(
@@ -479,7 +479,7 @@ public void executeSpecifyBibtex() {
479479
exceptionMessage));
480480
}
481481

482-
LOGGER.error("An exception occurred when parsing Bib(La)Tex entries.", exception);
482+
LOGGER.error("An exception occurred when parsing Bib(La)TeX entries.", exception);
483483

484484
executing.set(false);
485485
});
@@ -493,7 +493,7 @@ public void executeSpecifyBibtex() {
493493
Localization.lang(
494494
"An unknown error has occurred.\n" +
495495
"Entries may need to be added manually."));
496-
LOGGER.error("An invalid result was returned when parsing Bib(La)Tex entries.");
496+
LOGGER.error("An invalid result was returned when parsing Bib(La)TeX entries.");
497497
executing.set(false);
498498
return;
499499
}

jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
import org.jabref.logic.preferences.AutoCompleteFirstNameMode;
6363
import org.jabref.logic.preferences.JabRefCliPreferences;
6464
import org.jabref.logic.preview.PreviewLayout;
65-
import org.jabref.logic.util.StandardFileType;
6665
import org.jabref.logic.util.strings.StringUtil;
6766
import org.jabref.model.entry.BibEntryTypesManager;
6867
import org.jabref.model.entry.field.Field;
@@ -266,12 +265,6 @@ private JabRefGuiPreferences() {
266265
defaults.put(AUTOCOMPLETER_COMPLETE_FIELDS, "author;editor;title;journal;publisher;keywords;crossref;related;entryset");
267266
// endregion
268267

269-
// region unlinkedFilesDialogPreferences
270-
defaults.put(UNLINKED_FILES_SELECTED_EXTENSION, StandardFileType.ANY_FILE.getName());
271-
defaults.put(UNLINKED_FILES_SELECTED_DATE_RANGE, DateRange.ALL_TIME.name());
272-
defaults.put(UNLINKED_FILES_SELECTED_SORT, ExternalFileSorter.DEFAULT.name());
273-
// endregion
274-
275268
// region ExternalApplicationsPreferences
276269
defaults.put(EXTERNAL_FILE_TYPES, "");
277270
defaults.put(EMAIL_SUBJECT, Localization.lang("References"));
@@ -397,6 +390,7 @@ public void clear() throws BackingStoreException {
397390
getWorkspacePreferences().setAll(WorkspacePreferences.getDefault());
398391
getGuiPreferences().setAll(CoreGuiPreferences.getDefault());
399392
getDonationPreferences().setAll(DonationPreferences.getDefault());
393+
getUnlinkedFilesDialogPreferences().setAll(UnlinkedFilesDialogPreferences.getDefault());
400394
getNewEntryPreferences().setAll(NewEntryPreferences.getDefault());
401395
getSpecialFieldsPreferences().setAll(SpecialFieldsPreferences.getDefault());
402396
}
@@ -409,6 +403,7 @@ public void importPreferences(Path path) throws JabRefException {
409403
getWorkspacePreferences().setAll(getWorkspacePreferencesFromBackingStore(getWorkspacePreferences()));
410404
getGuiPreferences().setAll(getCoreGuiPreferencesFromBackingStore(getGuiPreferences()));
411405
getDonationPreferences().setAll(getDonationPreferencesFromBackingStore(getDonationPreferences()));
406+
getUnlinkedFilesDialogPreferences().setAll(UnlinkedFilesDialogPreferences.getDefault());
412407
getNewEntryPreferences().setAll(getNewEntryPreferencesFromBackingStore(getNewEntryPreferences()));
413408
getSpecialFieldsPreferences().setAll(getSpecialFieldsPreferencesFromBackingStore(getSpecialFieldsPreferences()));
414409
}
@@ -674,17 +669,21 @@ private WorkspacePreferences getWorkspacePreferencesFromBackingStore(WorkspacePr
674669
getStringList(SELECTED_SLR_CATALOGS));
675670
}
676671

672+
private UnlinkedFilesDialogPreferences getUnlinkedFilesDialogPreferencesFromBackingStore(UnlinkedFilesDialogPreferences defaults) {
673+
return new UnlinkedFilesDialogPreferences(
674+
get(UNLINKED_FILES_SELECTED_EXTENSION, defaults.getUnlinkedFilesSelectedExtension()),
675+
DateRange.parse(get(UNLINKED_FILES_SELECTED_DATE_RANGE, defaults.getUnlinkedFilesSelectedDateRange().name())),
676+
ExternalFileSorter.parse(get(UNLINKED_FILES_SELECTED_SORT, defaults.getUnlinkedFilesSelectedSort().name()))
677+
);
678+
}
679+
677680
@Override
678681
public UnlinkedFilesDialogPreferences getUnlinkedFilesDialogPreferences() {
679682
if (unlinkedFilesDialogPreferences != null) {
680683
return unlinkedFilesDialogPreferences;
681684
}
682685

683-
unlinkedFilesDialogPreferences = new UnlinkedFilesDialogPreferences(
684-
get(UNLINKED_FILES_SELECTED_EXTENSION),
685-
DateRange.parse(get(UNLINKED_FILES_SELECTED_DATE_RANGE)),
686-
ExternalFileSorter.parse(get(UNLINKED_FILES_SELECTED_SORT))
687-
);
686+
unlinkedFilesDialogPreferences = getUnlinkedFilesDialogPreferencesFromBackingStore(UnlinkedFilesDialogPreferences.getDefault());
688687

689688
EasyBind.listen(unlinkedFilesDialogPreferences.unlinkedFilesSelectedExtensionProperty(), (obs, oldValue, newValue) -> put(UNLINKED_FILES_SELECTED_EXTENSION, newValue));
690689
EasyBind.listen(unlinkedFilesDialogPreferences.unlinkedFilesSelectedDateRangeProperty(), (obs, oldValue, newValue) -> put(UNLINKED_FILES_SELECTED_DATE_RANGE, newValue.name()));

jablib/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
All code in `src/main/java` is published as a Maven artifact at `org.jabref:jablib`.
55
Example code is made available at <https://github.com/JabRef/jabref/tree/main/jablib-examples>.
66

7-
You can browse JavaDoc at APIdia: <https://apidia.net/java/JabRef/6.0-snapshot-2025-12-02/?cls=org.jabref.jablib-module>.
7+
You can browse JavaDoc at APIdia: [latest development version](https://apidia.net/java/JabRef/jablib/main) and [latest released version](https://apidia.net/java/JabRef/jablib/latest).
88

99
## Development information
1010

0 commit comments

Comments
 (0)