Skip to content

Add support for loading .blg warnings in Integrity Check Dialog#12866

Merged
koppor merged 21 commits into
JabRef:mainfrom
wanling0000:feature/11998-blg-integrity
Apr 18, 2025
Merged

Add support for loading .blg warnings in Integrity Check Dialog#12866
koppor merged 21 commits into
JabRef:mainfrom
wanling0000:feature/11998-blg-integrity

Conversation

@wanling0000

@wanling0000 wanling0000 commented Mar 31, 2025

Copy link
Copy Markdown
Collaborator

Closes #11998

This PR adds support for: Check integrity: Support for BibTeX .blg files


Users can now:

  • Automatically load the default .blg file from the same folder as the .bib file;
  • Select a custom .blg file via Browse and load BibTeX warnings;
  • Click Reset to revert to the default .blg path;
  • Replace old .blg warnings when switching files (i.e., only show current .blg file's messages);
  • Persist .blg file path in MetaData (saved along with .bib file).

Change Overview

Layer Component Responsibility
Model BibWarning Represent parsed .blg warning
MetaData.get/set/clearBlgFilePath() Store user-defined .blg file path
Logic BibtexLogParser Parse .blg lines into BibWarning objects
BibWarningToIntegrityMessageConverter Convert warnings to IntegrityMessage
BibLogPathResolver Resolve .blg file path: user-defined or fallback
MetaDataParser Add support for BLG_FILE_PATH in .bib metadata
MetaDataSerializer Serialize .blg file paths into metadata
ViewModel BibLogSettingsViewModel Wraps logic and exposes path + warning state to UI
UI BibLogSettingsPane.fxml Browse/Reset path selection UI
BibLogSettingsPane.java Controller handling Browse/Reset and notifying changes
IntegrityCheckDialog.java Load .blg warnings and refresh table display
IntegrityCheckAction.java Include .blg warnings during initial dialog show

Screenshots

Snipaste_2025-03-31_20-18-08


❓Limitations of .blg Parsing – I would appreciate feedback

Right now, the .blg parser in this PR (BibtexLogParser) only supports a very simple warning format:

Warning--[message] in [entryKey]

But I found that in real .blg files, there are other kinds of warnings that don’t follow this format.
For example, in this GitHub issue: James-Yu/LaTeX-Workshop#2324

Warning--string name "ccr" is undefined
--line 35 of file paper.bib

This kind of warning does not point to a specific entry or field, so currently it is ignored by the parser.

  • In this PR, I used KEY_FIELD as a fallback when the field is missing. This is just a temporary solution so the UI can still display something. But the UI currently assumes that every IntegrityMessage has a field, otherwise it may not work properly.

I’m wondering:

Would it make sense to introduce an interface like IntegrityMessage, or is it better to wait and see more .blg formats before making this change?

At the moment I don’t have many .blg test files, so I’m not sure how common these “fieldless” warnings are.
If you have any suggestions on how to investigate this, I’d be happy to look into it!

Any feedback or thoughts on this would be really helpful, thank you!


Mandatory checks

  • I own the copyright of the code submitted and I license it under the MIT license
  • Change in CHANGELOG.md described in a way that is understandable for the average user (if change is visible to the user)
  • Tests created for changes (if applicable)
  • Manually tested changed features in running JabRef (always required)
  • Screenshots added in PR description (if change is visible to the user)
  • Checked developer's documentation: Is the information available and up to date? If not, I outlined it in this pull request.
  • Checked documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request to the documentation repository.

Comment on lines +1197 to +1199
public DialogService getDialogService() {
return dialogService;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new public method 'getDialogService' returns 'null' if 'dialogService' is not initialized. It should return an Optional to avoid potential NullPointerExceptions.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified to pass DialogService from the constructor parameter

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do DI by constructor. Getters for singletons are a no go.

*/
private void loadBibLogSettingsPane() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("BibLogSettingsPane.fxml"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@calixtus Don't we use the afterburner fx stuff here?

/**
*
*/
public class BibWarning {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(quick initial comment)

Can this be a record? 😅

(and no empty JavaDoc please)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I've converted it to a record and removed the empty JavaDoc

*/
private Optional<BibWarning> parseWarningLine(String line) {
// TODO: Support additional warning formats
Pattern compile = Pattern.compile("^Warning--([a-zA-Z ]+) in ([^\\s]+)$");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put this into private static final. - and use named groups 😅

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated as suggested 👍


@FXML
private void onBrowse() {
FileChooser fileChooser = new FileChooser();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use jabref's dialogService for this: dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(path -> ...)

and with FileDialogConfiguration offers the Builder pattern.
(see e.g NewLibraryFromPdfAction)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, already updated as suggested

return blgFilePath;
}

public void setBlgFilePath(Path path) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method uses Objects.requireNonNull to check for null, which should be replaced with @nonnull annotation as per the guidelines.


List<IntegrityMessage> messages = BibWarningToIntegrityMessageConverter.convert(warnings, context);

assertEquals(1, messages.size());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assertion statements should not include the message parameter. The method name should already convey the expected behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trag-bot This is wrong

Comment thread src/main/java/org/jabref/gui/integrity/BibLogSettingsViewModel.java Outdated
Comment on lines +63 to +65
metaData.getBlgFilePaths().forEach((user, path) -> {
stringyMetaData.put(MetaData.BLG_FILE_PATH + "-" + user, Collections.singletonList(path.toString().trim()));
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of .blg file paths serialization in org.jabref.logic.exporter.MetaDataSerializer requires corresponding test updates to ensure the new functionality is covered.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already covered this change in the test method serializeUserSpecificBlgPath() ・ࡇ・

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this - do? Path, naming separator?

Maybe this could be a constant?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, the other meta data fields do it, too.

I am OK without constant - follow-up can refactor the whole MetaDataParser ^^

Comment thread src/test/java/org/jabref/logic/exporter/MetaDataSerializerTest.java
.put(MetaData.FILE_DIRECTORY_LATEX + '-' + user, Collections.singletonList(path.toString().trim())));
metaData.getVersionDBStructure().ifPresent(
versionDBStructure -> stringyMetaData.put(MetaData.VERSION_DB_STRUCT, Collections.singletonList(versionDBStructure.trim())));
metaData.getBlgFilePaths().forEach((user, path) -> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of serializing .blg file paths requires corresponding tests to be updated or added to ensure the new functionality is correctly implemented and verified.

Comment thread src/main/java/org/jabref/gui/integrity/BibLogSettingsPane.java

import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement for FileChooser is unnecessary as the dialogService is used for file dialogs, which aligns with the project's architecture guidelines.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, the comment of trag bot is correct.

@koppor

koppor commented Apr 4, 2025

Copy link
Copy Markdown
Member

Tried out now with a MWE generated by https://github.com/latextemplates/generator-latex-template. Works.

@wanling0000

Copy link
Copy Markdown
Collaborator Author

Tried out now with a MWE generated by https://github.com/latextemplates/generator-latex-template. Works.

Thanks @koppor

I noticed some of the checks are failing, I’ll take a look and try pushing a fix over this week. Let me know if there’s anything else I should work on :)

@wanling0000 wanling0000 marked this pull request as ready for review April 7, 2025 11:52
@wanling0000 wanling0000 marked this pull request as draft April 7, 2025 11:53
Comment thread src/main/java/org/jabref/gui/integrity/BibLogSettingsViewModel.java Outdated
Comment thread src/main/java/org/jabref/gui/integrity/BibLogSettingsViewModel.java Outdated
Comment thread src/main/java/org/jabref/logic/importer/util/MetaDataParser.java
Comment thread src/test/java/org/jabref/logic/biblog/BibLogPathResolverTest.java
Comment thread src/test/java/org/jabref/logic/biblog/BibLogPathResolverTest.java
Comment thread src/test/java/org/jabref/logic/biblog/BibLogPathResolverTest.java
* Ensures that the trailing semicolon (used as separator in .bib metadata) is handled and stripped properly.
*/
@Test
void parsesUserSpecificBlgPathSuccessfully() throws Exception {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method name 'parsesUserSpecificBlgPathSuccessfully' is not comprehensive enough. It should describe the test case more clearly without needing a @DisplayName annotation.

@wanling0000 wanling0000 requested a review from koppor April 15, 2025 13:02
private Runnable onBlgPathChanged;

public void initializeViewModel(BibDatabaseContext context, Runnable onBlgPathChanged) throws JabRefException {
if (context == null || context.getMetaData() == null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it necessary to check for nulls here?


public Path getInitialDirectory() {
return bibPath.flatMap(path -> Optional.ofNullable(path.getParent()))
.orElse(Path.of(System.getProperty("user.home")));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this cross-platform ("user.home")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not on Windows XP, but this is acceptable: https://stackoverflow.com/q/585534/873282

@wanling0000

Copy link
Copy Markdown
Collaborator Author

I only meant to leave a single comment here, but I think I accidentally triggered a full review by clicking the wrong button 😅

Also, I noticed that the Trag review has been marked as “in progress” for two days now, not sure if that’s expected or if something got stuck?

@subhramit

Copy link
Copy Markdown
Member

Also, I noticed that the Trag review has been marked as “in progress” for two days now, not sure if that’s expected or if something got stuck?

It sometimes does that - pushing another commit refreshes it.

@koppor koppor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some small comments towards a really JabRef-high-quality code 😅

Comment on lines +28 to +32
/**
* 1. Connects MetaData with the view.
* 2. Wraps .blg warnings as IntegrityMessages.
* 3. Supports file browsing and reset actions.
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either html or Markdown - I propose markdown

Suggested change
/**
* 1. Connects MetaData with the view.
* 2. Wraps .blg warnings as IntegrityMessages.
* 3. Supports file browsing and reset actions.
*/
/// 1. Connects MetaData with the view.
/// 2. Wraps .blg warnings as IntegrityMessages.
/// 3. Supports file browsing and reset actions.

Comment on lines +49 to +53
this.path.set(resolvedPath.toString());
if (metaData.getBlgFilePath(user).isEmpty()) {
metaData.setBlgFilePath(user, resolvedPath);
this.lastResolvedBlgPath = Optional.of(resolvedPath);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent seems to be off

Comment on lines +9 to +15
/**
* Resolves custom or default .blg path for this library.
*
* Priority:
* 1. User-defined path from MetaData
* 2. Default: same name as .bib file with .blg extension
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Resolves custom or default .blg path for this library.
*
* Priority:
* 1. User-defined path from MetaData
* 2. Default: same name as .bib file with .blg extension
*/
/// Resolves custom or default .blg path for this library.
///
/// Priority:
///
/// 1. User-defined path from MetaData
/// 2. Default: same name as .bib file with .blg extension

Comment on lines +16 to +28
/**
* Converts {@link BibWarning}s into {@link IntegrityMessage}s for integration with the existing integrity check UI.
*
* Notes:
* - The current IntegrityMessage interface expects a {@link BibEntry} and a {@link Field},
* but .blg warnings come from a different source and may not include a field.
* - For now, we map missing fields to a placeholder (InternalField.KEY_FIELD) to make it compatible with the UI.
* - This is a minimal MVP solution to reuse the Integrity tab without changing the UI structure.
*
* Future direction:
* - Consider defining a proper interface (e.g., IntegrityMessageWithField / WithoutField)
* to support warnings without fields cleanly.
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also convert to Markdown (to have the notes list rendered properly)

Comment on lines +63 to +65
metaData.getBlgFilePaths().forEach((user, path) -> {
stringyMetaData.put(MetaData.BLG_FILE_PATH + "-" + user, Collections.singletonList(path.toString().trim()));
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, the other meta data fields do it, too.

I am OK without constant - follow-up can refactor the whole MetaDataParser ^^

Comment on lines +39 to +49
assertEquals(2, messages.size());

IntegrityMessage msg1 = messages.getFirst();
assertEquals("empty journal", msg1.message());
assertEquals(firstEntry, msg1.entry());
assertEquals("journal", msg1.field().getName());

IntegrityMessage msg2 = messages.get(1);
assertEquals("empty year", msg2.message());
assertEquals(secondEntry, msg2.entry());
assertEquals("year", msg2.field().getName());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to compare two lists directly? Or are IntegrityMessage objects not good with equals?

Comment on lines +42 to +49
BibLogPathResolver.resolve(metaData, bibPath, user)
.ifPresent(resolvedPath -> {
this.path.set(resolvedPath.toString());
if (metaData.getBlgFilePath(user).isEmpty()) {
metaData.setBlgFilePath(user, resolvedPath);
this.lastResolvedBlgPath = Optional.of(resolvedPath);
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code does not follow the fail-fast principle. It should return early if the condition is not met, instead of nesting logic inside an if statement.

Comment on lines +101 to +114
public void resetBlgFilePath() {
metaData.clearBlgFilePath(user);
userManuallySelectedBlgFile = false;
Optional<Path> resolved = BibLogPathResolver.resolve(metaData, bibPath, user);
if (resolved.isEmpty()) {
path.set("");
lastResolvedBlgPath = Optional.empty();
return;
}

Path resolvedPath = resolved.get();
path.set(resolvedPath.toString());
lastResolvedBlgPath = Optional.of(resolvedPath);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method does not follow the fail-fast principle. It should return early if the condition is not met, instead of nesting logic inside an if statement.

Comment on lines +35 to +37
if (context.getDatabase().getEntryByCitationKey(bibWarning.entryKey()).isEmpty()) {
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code should follow the fail fast principle by immediately handling invalid states and returning early instead of nesting logic inside else branches. This code should return early if the entry is not found.

new IntegrityMessage("empty year", secondEntry, FieldFactory.parseField("year"))
);

assertEquals(expectedMessages, actualMessages);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test uses assertEquals to compare lists of objects. It's better to assert the contents of objects directly to ensure more precise validation of the test results.

@wanling0000 wanling0000 requested a review from koppor April 18, 2025 10:36
@koppor

koppor commented Apr 18, 2025

Copy link
Copy Markdown
Member

@wanling0000 One last thing: submodules src/main/resources/csl-styles were modified - can you follow https://devdocs.jabref.org/code-howtos/faq.html#submodules to fix?

@trag-bot

trag-bot Bot commented Apr 18, 2025

Copy link
Copy Markdown

@trag-bot didn't find any issues in the code! ✅✨

@wanling0000

Copy link
Copy Markdown
Collaborator Author

@wanling0000 One last thing: submodules src/main/resources/csl-styles were modified - can you follow https://devdocs.jabref.org/code-howtos/faq.html#submodules to fix?

Fixed, thanks for the reminder!

@koppor koppor added this pull request to the merge queue Apr 18, 2025
Merged via the queue into JabRef:main with commit ccc379c Apr 18, 2025
@subhramit

Copy link
Copy Markdown
Member

@wanling0000 good work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Check integrity: Support for BibTeX .blg files

6 participants