Skip to content
Open
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv

### Added

- We added a per-library journal abbreviation preference to Library Properties, allowing automatic LTWA abbreviation on save. [#15495](https://github.com/JabRef/jabref/issues/15495)
- We added a related work text extractor, which finds and inserts the related work text into bib entries from references in the texts. [#9840](https://github.com/JabRef/jabref/issues/9840)
- We added a hover button on group rows to quickly add a new group or subgroup. [#12289](https://github.com/JabRef/jabref/issues/12289)
- We added a shorthand for protecting terms in the fields: user can now select a text and type a opening curling brace to quickly wrap the selection in braces. [#15442](https://github.com/JabRef/jabref/pull/15442)
Expand Down
2 changes: 1 addition & 1 deletion jabgui/src/main/java/org/jabref/gui/LibraryTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ private boolean confirmClose() {

if (buttonType.equals(saveChanges)) {
try {
SaveDatabaseAction saveAction = new SaveDatabaseAction(this, dialogService, preferences, Injector.instantiateModelOrService(BibEntryTypesManager.class), stateManager);
SaveDatabaseAction saveAction = new SaveDatabaseAction(this, dialogService, preferences, Injector.instantiateModelOrService(BibEntryTypesManager.class), stateManager, Injector.instantiateModelOrService(JournalAbbreviationRepository.class));
if (saveAction.save()) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.exporter.SaveDatabaseAction;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.model.database.event.AutosaveEvent;
import org.jabref.model.entry.BibEntryTypesManager;

import com.airhacks.afterburner.injection.Injector;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -20,7 +22,7 @@ public class AutosaveUiManager {
private final SaveDatabaseAction saveDatabaseAction;

public AutosaveUiManager(LibraryTab libraryTab, DialogService dialogService, GuiPreferences preferences, BibEntryTypesManager entryTypesManager, StateManager stateManager) {
this.saveDatabaseAction = new SaveDatabaseAction(libraryTab, dialogService, preferences, entryTypesManager, stateManager);
this.saveDatabaseAction = new SaveDatabaseAction(libraryTab, dialogService, preferences, entryTypesManager, stateManager, Injector.instantiateModelOrService(JournalAbbreviationRepository.class));
}

@Subscribe
Expand Down
4 changes: 3 additions & 1 deletion jabgui/src/main/java/org/jabref/gui/exporter/SaveAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.model.entry.BibEntryTypesManager;

import com.airhacks.afterburner.injection.Injector;
Expand Down Expand Up @@ -49,7 +50,8 @@ public void execute() {
dialogService,
preferences,
Injector.instantiateModelOrService(BibEntryTypesManager.class),
stateManager);
stateManager,
Injector.instantiateModelOrService(JournalAbbreviationRepository.class));

switch (saveMethod) {
case SAVE ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntryTypesManager;

Expand Down Expand Up @@ -35,7 +36,7 @@ public void execute() {
dialogService.notify(Localization.lang("Saving all libraries..."));

for (LibraryTab libraryTab : tabsSupplier.get()) {
SaveDatabaseAction saveDatabaseAction = new SaveDatabaseAction(libraryTab, dialogService, preferences, Injector.instantiateModelOrService(BibEntryTypesManager.class), stateManager);
SaveDatabaseAction saveDatabaseAction = new SaveDatabaseAction(libraryTab, dialogService, preferences, Injector.instantiateModelOrService(BibEntryTypesManager.class), stateManager, Injector.instantiateModelOrService(JournalAbbreviationRepository.class));
boolean saveResult = saveDatabaseAction.save();
if (!saveResult) {
dialogService.notify(Localization.lang("Could not save file."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
Expand All @@ -27,18 +28,22 @@
import org.jabref.gui.maintable.columns.MainTableColumn;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.cleanup.AbbreviateJournalCleanup;
import org.jabref.logic.exporter.AtomicFileWriter;
import org.jabref.logic.exporter.BibDatabaseWriter;
import org.jabref.logic.exporter.BibWriter;
import org.jabref.logic.exporter.SaveException;
import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.os.OS;
import org.jabref.logic.shared.DatabaseLocation;
import org.jabref.logic.shared.prefs.SharedDatabasePreferences;
import org.jabref.logic.util.StandardFileType;
import org.jabref.model.FieldChange;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.event.ChangePropagation;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.metadata.SaveOrder;
import org.jabref.model.metadata.SelfContainedSaveOrder;
Expand All @@ -59,6 +64,7 @@ public class SaveDatabaseAction {
private final GuiPreferences preferences;
private final BibEntryTypesManager entryTypesManager;
private final StateManager stateManager;
private final JournalAbbreviationRepository journalAbbreviationRepository;

public enum SaveDatabaseMode {
SILENT, NORMAL
Expand All @@ -68,12 +74,14 @@ public SaveDatabaseAction(LibraryTab libraryTab,
DialogService dialogService,
GuiPreferences preferences,
BibEntryTypesManager entryTypesManager,
StateManager stateManager) {
StateManager stateManager,
JournalAbbreviationRepository journalAbbreviationRepository) {
this.libraryTab = libraryTab;
this.dialogService = dialogService;
this.preferences = preferences;
this.entryTypesManager = entryTypesManager;
this.stateManager = stateManager;
this.journalAbbreviationRepository = journalAbbreviationRepository;
}

public boolean save() {
Expand Down Expand Up @@ -260,13 +268,29 @@ private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding,
preferences.getCitationKeyPatternPreferences(),
entryTypesManager);

List<BibEntry> entriesToAbbreviate = selectedOnly
? libraryTab.getSelectedEntries()
: bibDatabaseContext.getDatabase().getEntries();
List<FieldChange> abbreviationChanges = bibDatabaseContext.getMetaData()
.getLibraryAbbreviationType()
.map(abbreviationType -> {
boolean useFJournal = preferences.getJournalAbbreviationPreferences().shouldUseFJournalField();
AbbreviateJournalCleanup cleanup = new AbbreviateJournalCleanup(
bibDatabaseContext.getDatabase(), journalAbbreviationRepository, abbreviationType, useFJournal);
return entriesToAbbreviate.stream()
.flatMap(entry -> cleanup.cleanup(entry).stream())
.toList();
})
.orElse(List.of());

if (selectedOnly) {
databaseWriter.writePartOfDatabase(bibDatabaseContext, libraryTab.getSelectedEntries());
} else {
databaseWriter.writeDatabase(bibDatabaseContext);
}

libraryTab.registerUndoableChanges(databaseWriter.getSaveActionsFieldChanges());
libraryTab.registerUndoableChanges(
Stream.concat(databaseWriter.getSaveActionsFieldChanges().stream(), abbreviationChanges.stream()).toList());
Comment on lines +274 to +293
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

2. Cleanup logic added to gui 📘 Rule violation ⚙ Maintainability

SaveDatabaseAction (GUI layer) now performs journal abbreviation cleanup over all entries, which
is non-GUI/business logic. This violates the layering guideline and makes the GUI responsible for
logic behavior.
Agent Prompt
## Issue description
GUI-layer `SaveDatabaseAction` now contains business logic for abbreviating journals on save.

## Issue Context
Layering rules require business logic to be implemented in `org.jabref.logic`, with GUI code acting as wiring/gateway.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java[276-288]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


if (fileWriter.hasEncodingProblems()) {
saveWithDifferentEncoding(file, selectedOnly, encoding, fileWriter.getEncodingProblems(), saveType, saveOrder);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.jabref.gui.libraryproperties.saving;

import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.util.StringConverter;

import org.jabref.gui.commonfxcontrols.FieldFormatterCleanupsPanel;
import org.jabref.gui.commonfxcontrols.SaveOrderConfigPanel;
import org.jabref.gui.libraryproperties.AbstractPropertiesTabView;
import org.jabref.gui.libraryproperties.PropertiesTab;
import org.jabref.logic.journals.AbbreviationType;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.model.database.BibDatabaseContext;
Expand All @@ -19,6 +23,7 @@ public class SavingPropertiesView extends AbstractPropertiesTabView<SavingProper
@FXML private CheckBox protect;
@FXML private SaveOrderConfigPanel saveOrderConfigPanel;
@FXML private FieldFormatterCleanupsPanel fieldFormatterCleanupsPanel;
@FXML private ComboBox<AbbreviationType> journalAbbreviationOnSave;

@Inject private CliPreferences preferences;

Expand Down Expand Up @@ -49,5 +54,33 @@ public void initialize() {

fieldFormatterCleanupsPanel.cleanupsDisableProperty().bindBidirectional(viewModel.cleanupsDisableProperty());
fieldFormatterCleanupsPanel.cleanupsProperty().bindBidirectional(viewModel.cleanupsProperty());

journalAbbreviationOnSave.setItems(FXCollections.observableArrayList(
null, AbbreviationType.DEFAULT, AbbreviationType.DOTLESS,
AbbreviationType.SHORTEST_UNIQUE, AbbreviationType.LTWA));
journalAbbreviationOnSave.setConverter(new StringConverter<>() {
@Override
public String toString(AbbreviationType type) {
if (type == null) {
return Localization.lang("None (use global setting)");
}
return switch (type) {
case DEFAULT ->
Localization.lang("Abbreviate (default)");
case DOTLESS ->
Localization.lang("Abbreviate (dotless)");
case SHORTEST_UNIQUE ->
Localization.lang("Abbreviate (shortest unique)");
case LTWA ->
Localization.lang("Abbreviate (LTWA)");
};
}
Comment on lines +67 to +77
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I hope these options sound correct.


@Override
public AbbreviationType fromString(String string) {
return null;
}
});
journalAbbreviationOnSave.valueProperty().bindBidirectional(viewModel.journalAbbreviationOnSaveProperty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;

import org.jabref.gui.commonfxcontrols.SortCriterionViewModel;
import org.jabref.gui.libraryproperties.PropertiesTabViewModel;
import org.jabref.logic.cleanup.CleanupPreferences;
import org.jabref.logic.cleanup.FieldFormatterCleanup;
import org.jabref.logic.cleanup.FieldFormatterCleanupActions;
import org.jabref.logic.journals.AbbreviationType;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.field.Field;
Expand Down Expand Up @@ -49,6 +52,9 @@ public class SavingPropertiesViewModel implements PropertiesTabViewModel {
private final BooleanProperty cleanupsDisableProperty = new SimpleBooleanProperty();
private final ListProperty<FieldFormatterCleanup> cleanupsProperty = new SimpleListProperty<>(FXCollections.emptyObservableList());

// Journal abbreviation on save
private final ObjectProperty<AbbreviationType> journalAbbreviationOnSaveProperty = new SimpleObjectProperty<>();

private final BibDatabaseContext databaseContext;
private final MetaData initialMetaData;
private final SaveOrder saveOrder;
Expand Down Expand Up @@ -101,6 +107,8 @@ public void setValues() {
cleanupsDisableProperty.setValue(!defaultPreset.getFieldFormatterCleanups().isEnabled());
cleanupsProperty.setValue(FXCollections.observableArrayList(defaultPreset.getFieldFormatterCleanups().getConfiguredActions()));
});

journalAbbreviationOnSaveProperty.setValue(initialMetaData.getLibraryAbbreviationType().orElse(null));
}

@Override
Expand Down Expand Up @@ -140,6 +148,13 @@ public void storeSettings() {
}
}

AbbreviationType abbreviationType = journalAbbreviationOnSaveProperty.getValue();
if (abbreviationType != null) {
newMetaData.setLibraryAbbreviationType(abbreviationType);
} else {
newMetaData.clearLibraryAbbreviationType();
}

databaseContext.setMetaData(newMetaData);
}

Expand Down Expand Up @@ -182,4 +197,8 @@ public BooleanProperty cleanupsDisableProperty() {
public ListProperty<FieldFormatterCleanup> cleanupsProperty() {
return cleanupsProperty;
}

public ObjectProperty<AbbreviationType> journalAbbreviationOnSaveProperty() {
return journalAbbreviationOnSaveProperty;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.jabref.gui.util.FileFilterConverter;
import org.jabref.logic.ai.AiService;
import org.jabref.logic.help.HelpFile;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.shared.DBMSConnectionProperties;
import org.jabref.logic.shared.DBMSConnectionPropertiesBuilder;
Expand Down Expand Up @@ -213,7 +214,8 @@ private boolean openSharedDatabase(DBMSConnectionProperties connectionProperties
dialogService,
preferences,
Injector.instantiateModelOrService(BibEntryTypesManager.class),
stateManager
stateManager,
Injector.instantiateModelOrService(JournalAbbreviationRepository.class)
).saveAs(Path.of(folder.getValue()));
} catch (Throwable e) {
LOGGER.error("Error while saving the database", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.commonfxcontrols.FieldFormatterCleanupsPanel?>
<?import org.jabref.gui.commonfxcontrols.SaveOrderConfigPanel?>
Expand All @@ -18,6 +20,12 @@

<Label styleClass="sectionHeader" text="%Save actions"/>
<FieldFormatterCleanupsPanel fx:id="fieldFormatterCleanupsPanel" />

<Label styleClass="sectionHeader" text="%Journal abbreviation on save"/>
<HBox spacing="8.0" alignment="CENTER_LEFT">
<Label text="%Abbreviation style"/>
<ComboBox fx:id="journalAbbreviationOnSave"/>
</HBox>
<padding>
<Insets bottom="12.0" />
</padding>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.jabref.logic.exporter.BibDatabaseWriter;
import org.jabref.logic.exporter.ExportPreferences;
import org.jabref.logic.exporter.SaveConfiguration;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.shared.DatabaseLocation;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
Expand Down Expand Up @@ -64,7 +65,7 @@ void setUp() {
when(filePreferences.getWorkingDirectory()).thenReturn(Path.of(TEST_BIBTEX_LIBRARY_LOCATION));
when(preferences.getFilePreferences()).thenReturn(filePreferences);
when(preferences.getExportPreferences()).thenReturn(mock(ExportPreferences.class));
saveDatabaseAction = spy(new SaveDatabaseAction(libraryTab, dialogService, preferences, mock(BibEntryTypesManager.class), stateManager));
saveDatabaseAction = spy(new SaveDatabaseAction(libraryTab, dialogService, preferences, mock(BibEntryTypesManager.class), stateManager, mock(JournalAbbreviationRepository.class)));
}

@Test
Expand Down Expand Up @@ -133,7 +134,7 @@ private SaveDatabaseAction createSaveDatabaseActionForBibDatabase(BibDatabase da
when(libraryTab.getBibDatabaseContext()).thenReturn(dbContext);
when(libraryTab.getUndoManager()).thenReturn(mock(CountingUndoManager.class));
when(libraryTab.getBibDatabaseContext()).thenReturn(dbContext);
saveDatabaseAction = new SaveDatabaseAction(libraryTab, dialogService, preferences, mock(BibEntryTypesManager.class), stateManager);
saveDatabaseAction = new SaveDatabaseAction(libraryTab, dialogService, preferences, mock(BibEntryTypesManager.class), stateManager, mock(JournalAbbreviationRepository.class));
return saveDatabaseAction;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static Map<String, String> getSerializedStringMap(MetaData metaData,
stringyMetaData.putAll(serializeCiteKeyPatterns(metaData, globalCiteKeyPatterns));
metaData.getMode().ifPresent(
mode -> stringyMetaData.put(MetaData.DATABASE_TYPE, List.of(mode.getAsString())));
metaData.getLibraryAbbreviationType().ifPresent(
abbreviationType -> stringyMetaData.put(MetaData.LIBRARY_ABBREVIATION_TYPE, List.of(abbreviationType.name())));
metaData.getLibrarySpecificFileDirectory().ifPresent(
path -> stringyMetaData.put(MetaData.FILE_DIRECTORY, List.of(path.trim())));
metaData.getUserFileDirectories().forEach((user, path) -> stringyMetaData
Expand Down
Loading
Loading