Skip to content

Commit ccc379c

Browse files
authored
Add support for loading .blg warnings in Integrity Check Dialog (#12866)
* Add .blg warning support in IntegrityCheckDialog with Browse/Reset and path resolution * Apply review suggestions * Apply changes based on review feedback * add missing localization key for BibTeX log files * apply OpenRewrite suggestions * Fix preferences and Reset logic for .blg file settings * Apply review suggestions * Apply review suggestions * Fix failing unit tests * Apply review suggestions * Apply review suggestions and improve separation of concerns for blg warning panel * Fix localization key + apply bot suggestions * Apply bot suggestions on fast-fail, fix Rewrite and test failures * Remove star imports * Remove unnecessary @nonnull annotations and fix misleading UI warning logic * Apply review suggestions: simplify test and update Javadoc * Revert unintended submodule change to csl-styles
1 parent ad24ace commit ccc379c

24 files changed

+832
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
2727
- We added a feature for enabling drag-and-drop of files into groups [#12540](https://github.com/JabRef/jabref/issues/12540)
2828
- We added support for reordering keywords via drag and drop, automatic alphabetical ordering, and improved pasting and editing functionalities in the keyword editor. [#10984](https://github.com/JabRef/jabref/issues/10984)
2929
- We added a new functionality where author names having multiple spaces in-between will be considered as separate user block as it does for " and ". [#12701](https://github.com/JabRef/jabref/issues/12701)
30+
- We added support for loading and displaying BibTeX .blg warnings in the Check integrity dialog, with custom path selection and metadata persistence. [#11998](https://github.com/JabRef/jabref/issues/11998)
3031
- We added an option to choose whether to open the file explorer in the files directory or in the last opened directory when attaching files. [#12554](https://github.com/JabRef/jabref/issues/12554)
3132
- We enhanced support for parsing XMP metadata from PDF files. [#12829](https://github.com/JabRef/jabref/issues/12829)
3233
- We added a "Preview" header in the JStyles tab in the "Select style" dialog, to make it consistent with the CSL styles tab. [#12838](https://github.com/JabRef/jabref/pull/12838)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<?import javafx.scene.control.Button?>
4+
<?import javafx.scene.control.Label?>
5+
<?import javafx.scene.control.TextField?>
6+
<?import javafx.scene.layout.HBox?>
7+
8+
<HBox xmlns="http://javafx.com/javafx"
9+
xmlns:fx="http://javafx.com/fxml"
10+
spacing="10"
11+
alignment="CENTER_LEFT"
12+
fx:controller="org.jabref.gui.integrity.BibLogSettingsPane"
13+
fx:id="bibLogSettingsPane">
14+
<Label text="BibTeX log (.blg) file:"/>
15+
<TextField fx:id="pathField" prefWidth="300"/>
16+
<Button onAction="#onBrowse" text="Browse..."/>
17+
<Button onAction="#onReset" text="Reset"/>
18+
</HBox>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.jabref.gui.integrity;
2+
3+
import java.nio.file.Path;
4+
5+
import javafx.collections.ObservableList;
6+
import javafx.fxml.FXML;
7+
import javafx.scene.control.TextField;
8+
9+
import org.jabref.gui.DialogService;
10+
import org.jabref.gui.util.FileDialogConfiguration;
11+
import org.jabref.logic.JabRefException;
12+
import org.jabref.logic.integrity.IntegrityMessage;
13+
import org.jabref.logic.l10n.Localization;
14+
import org.jabref.logic.util.StandardFileType;
15+
import org.jabref.model.database.BibDatabaseContext;
16+
17+
import jakarta.inject.Inject;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
21+
/**
22+
* Controller for the .blg file settings panel.
23+
*
24+
* Binds the path text field to the ViewModel,
25+
* and handles browse/reset button actions.
26+
*/
27+
public class BibLogSettingsPane {
28+
private static final Logger LOGGER = LoggerFactory.getLogger(BibLogSettingsPane.class);
29+
@FXML
30+
private TextField pathField;
31+
private BibLogSettingsViewModel viewModel;
32+
@Inject private DialogService dialogService;
33+
private Runnable onBlgPathChanged;
34+
35+
public void initializeViewModel(BibDatabaseContext context, Runnable onBlgPathChanged) throws JabRefException {
36+
this.onBlgPathChanged = onBlgPathChanged;
37+
this.viewModel = new BibLogSettingsViewModel(context.getMetaData(), context.getDatabasePath());
38+
pathField.textProperty().bindBidirectional(viewModel.pathProperty());
39+
viewModel.getBlgWarnings(context);
40+
}
41+
42+
public ObservableList<IntegrityMessage> getBlgWarnings() {
43+
return viewModel.getBlgWarningsObservable();
44+
}
45+
46+
public void refreshWarnings(BibDatabaseContext context) throws JabRefException {
47+
viewModel.getBlgWarnings(context);
48+
}
49+
50+
@FXML
51+
private void onBrowse() {
52+
FileDialogConfiguration fileDialogConfiguration = createBlgFileDialogConfig();
53+
dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(path -> {
54+
viewModel.setBlgFilePath(path);
55+
notifyPathChanged();
56+
});
57+
}
58+
59+
@FXML
60+
private void onReset() {
61+
viewModel.resetBlgFilePath();
62+
notifyPathChanged();
63+
}
64+
65+
private void notifyPathChanged() {
66+
if (onBlgPathChanged != null) {
67+
onBlgPathChanged.run();
68+
}
69+
}
70+
71+
public BibLogSettingsViewModel getViewModel() {
72+
return viewModel;
73+
}
74+
75+
private FileDialogConfiguration createBlgFileDialogConfig() {
76+
Path initialDir = viewModel.getInitialDirectory();
77+
FileDialogConfiguration config = new FileDialogConfiguration.Builder()
78+
.addExtensionFilter(Localization.lang("BibTeX log files"), StandardFileType.BLG)
79+
.withDefaultExtension(Localization.lang("BibTeX log files"), StandardFileType.BLG)
80+
.withInitialDirectory(viewModel.getInitialDirectory())
81+
.build();
82+
return config;
83+
}
84+
85+
public boolean wasBlgFileManuallySelected() {
86+
return viewModel.wasBlgFileManuallySelected();
87+
}
88+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package org.jabref.gui.integrity;
2+
3+
import java.io.IOException;
4+
import java.nio.file.Files;
5+
import java.nio.file.Path;
6+
import java.util.List;
7+
import java.util.Optional;
8+
9+
import javafx.beans.property.SimpleStringProperty;
10+
import javafx.beans.property.StringProperty;
11+
import javafx.collections.FXCollections;
12+
import javafx.collections.ObservableList;
13+
14+
import org.jabref.gui.AbstractViewModel;
15+
import org.jabref.logic.JabRefException;
16+
import org.jabref.logic.biblog.BibLogPathResolver;
17+
import org.jabref.logic.biblog.BibWarningToIntegrityMessageConverter;
18+
import org.jabref.logic.biblog.BibtexLogParser;
19+
import org.jabref.logic.integrity.IntegrityMessage;
20+
import org.jabref.logic.l10n.Localization;
21+
import org.jabref.model.biblog.BibWarning;
22+
import org.jabref.model.database.BibDatabaseContext;
23+
import org.jabref.model.metadata.MetaData;
24+
25+
/// 1. Connects MetaData with the view.
26+
/// 2. Wraps .blg warnings as IntegrityMessages.
27+
/// 3. Supports file browsing and reset actions.
28+
public class BibLogSettingsViewModel extends AbstractViewModel {
29+
private final ObservableList<IntegrityMessage> blgWarnings = FXCollections.observableArrayList();
30+
private final StringProperty path = new SimpleStringProperty("");
31+
private final MetaData metaData;
32+
private final Optional<Path> bibPath;
33+
private final String user;
34+
private Optional<Path> lastResolvedBlgPath = Optional.empty();
35+
private boolean userManuallySelectedBlgFile = false;
36+
37+
public BibLogSettingsViewModel(MetaData metaData, Optional<Path> bibPath) {
38+
this.metaData = metaData;
39+
this.bibPath = bibPath;
40+
this.user = System.getProperty("user.name");
41+
42+
BibLogPathResolver.resolve(metaData, bibPath, user)
43+
.ifPresent(resolvedPath -> {
44+
this.path.set(resolvedPath.toString());
45+
if (metaData.getBlgFilePath(user).isEmpty()) {
46+
metaData.setBlgFilePath(user, resolvedPath);
47+
this.lastResolvedBlgPath = Optional.of(resolvedPath);
48+
}
49+
});
50+
}
51+
52+
/**
53+
* Parses the .blg file (if it exists) into the observable list.
54+
*
55+
* @param databaseContext the current database context used to resolve citation keys in warnings.
56+
* @return An Optional containing the list of integrity messages if the file exists and can be parsed,
57+
* or an empty Optional if the file does not exist.
58+
* @throws JabRefException if the .blg file cannot be parsed or read
59+
*/
60+
public Optional<List<IntegrityMessage>> getBlgWarnings(BibDatabaseContext databaseContext) throws JabRefException {
61+
Optional<Path> resolved = getResolvedBlgPath();
62+
if (resolved.isEmpty()) {
63+
blgWarnings.clear();
64+
return Optional.empty();
65+
}
66+
67+
Path path = resolved.get();
68+
69+
this.lastResolvedBlgPath = Optional.of(path);
70+
try {
71+
BibtexLogParser parser = new BibtexLogParser();
72+
List<BibWarning> warnings = parser.parseBiblog(path);
73+
List<IntegrityMessage> newWarnings = BibWarningToIntegrityMessageConverter.convert(warnings, databaseContext);
74+
blgWarnings.setAll(newWarnings);
75+
return Optional.of(newWarnings);
76+
} catch (IOException e) {
77+
blgWarnings.clear();
78+
throw new JabRefException(
79+
"Failed to parse .blg file",
80+
Localization.lang("Could not read BibTeX log file. Please check the file path and try again."),
81+
e
82+
);
83+
}
84+
}
85+
86+
public ObservableList<IntegrityMessage> getBlgWarningsObservable() {
87+
return blgWarnings;
88+
}
89+
90+
public StringProperty pathProperty() {
91+
return path;
92+
}
93+
94+
public void setBlgFilePath(Path path) {
95+
metaData.setBlgFilePath(user, path);
96+
this.path.set(path.toString());
97+
this.lastResolvedBlgPath = Optional.of(path);
98+
this.userManuallySelectedBlgFile = true;
99+
}
100+
101+
public void resetBlgFilePath() {
102+
metaData.clearBlgFilePath(user);
103+
userManuallySelectedBlgFile = false;
104+
Optional<Path> resolved = BibLogPathResolver.resolve(metaData, bibPath, user);
105+
if (resolved.isEmpty()) {
106+
path.set("");
107+
lastResolvedBlgPath = Optional.empty();
108+
return;
109+
}
110+
111+
Path resolvedPath = resolved.get();
112+
path.set(resolvedPath.toString());
113+
lastResolvedBlgPath = Optional.of(resolvedPath);
114+
}
115+
116+
public Optional<Path> getResolvedBlgPath() {
117+
return BibLogPathResolver.resolve(metaData, bibPath, user)
118+
.filter(Files::exists);
119+
}
120+
121+
public Path getInitialDirectory() {
122+
return bibPath.flatMap(path -> Optional.ofNullable(path.getParent()))
123+
.orElse(Path.of(System.getProperty("user.home")));
124+
}
125+
126+
public boolean wasBlgFileManuallySelected() {
127+
return userManuallySelectedBlgFile;
128+
}
129+
}

src/main/java/org/jabref/gui/integrity/IntegrityCheckAction.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ protected List<IntegrityMessage> call() {
6969
result.addAll(check.checkEntry(entry));
7070
updateProgress(i, entries.size());
7171
}
72-
7372
return result;
7473
}
7574
};
@@ -78,7 +77,7 @@ protected List<IntegrityMessage> call() {
7877
if (messages.isEmpty()) {
7978
dialogService.notify(Localization.lang("No problems found."));
8079
} else {
81-
dialogService.showCustomDialogAndWait(new IntegrityCheckDialog(messages, tabSupplier.get()));
80+
dialogService.showCustomDialogAndWait(new IntegrityCheckDialog(messages, tabSupplier.get(), dialogService));
8281
}
8382
});
8483
task.setOnFailed(event -> dialogService.showErrorDialogAndWait("Integrity check failed.", task.getException()));

src/main/java/org/jabref/gui/integrity/IntegrityCheckDialog.fxml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
<?import javafx.scene.control.Button?>
44
<?import javafx.scene.control.ButtonType?>
55
<?import javafx.scene.control.DialogPane?>
6+
<?import javafx.scene.control.Label?>
67
<?import javafx.scene.control.MenuButton?>
78
<?import javafx.scene.control.TableColumn?>
89
<?import javafx.scene.control.TableView?>
10+
<?import javafx.scene.control.TextField?>
911
<?import javafx.scene.layout.HBox?>
1012
<?import javafx.scene.layout.VBox?>
1113
<DialogPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="600.0" prefWidth="700.0"
1214
xmlns="http://javafx.com/javafx/8.0.121" fx:controller="org.jabref.gui.integrity.IntegrityCheckDialog">
1315
<content>
14-
<VBox spacing="4.0">
16+
<VBox fx:id="dialogVBox" spacing="4.0">
1517
<TableView fx:id="messagesTable" prefHeight="550" prefWidth="700.0" VBox.vgrow="ALWAYS" HBox.hgrow="ALWAYS">
1618
<columns>
1719
<TableColumn fx:id="keyColumn" prefWidth="150.0" maxWidth="200" text="%Citation key"/>

0 commit comments

Comments
 (0)