From 5e8a4d5136ded988e1514e11d8cb6ff81467b5c1 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 2 Sep 2023 22:34:44 +0200 Subject: [PATCH 1/4] Fix mixup with YAML tags (#10287) --- .github/workflows/deployment-arm64.yml | 4 ++-- .github/workflows/deployment.yml | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deployment-arm64.yml b/.github/workflows/deployment-arm64.yml index d70fb31c04e2..38693c6fd8b5 100644 --- a/.github/workflows/deployment-arm64.yml +++ b/.github/workflows/deployment-arm64.yml @@ -163,7 +163,7 @@ jobs: xcrun notarytool submit build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-arm64.pkg --keychain-profile "notarytool-profile" --keychain ${{runner.temp}}/keychain/notarization.keychain --wait xcrun stapler staple build/distribution/JabRef-${{ steps.gitversion.outputs.Major }}.${{ steps.gitversion.outputs.Minor }}-arm64.pkg - name: Upload with rsync - if: ! startsWith(github.ref, 'refs/heads/gh-readonly-queue') + if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} shell: bash run: | mkdir ${{runner.temp}}/sshkey @@ -173,7 +173,7 @@ jobs: fi rsync -Pavz --itemize-changes --stats --partial-dir=/tmp/partial --rsync-path="mkdir -p /var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }} && rsync" -e 'ssh -p 9922 -i ~/.ssh/id_rsa' build/distribution/ jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ - name: Upload to GitHub workflow artifacts store - if: ! startsWith(github.ref, 'refs/heads/gh-readonly-queue') + if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} uses: actions/upload-artifact@v3 with: name: JabRef-${{ matrix.displayName }} diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index d4704ae32397..c439988a2263 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -229,7 +229,7 @@ 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: Upload to builds.jabref.org (ubuntu) - if: (matrix.os == 'ubuntu-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (! startsWith(github.ref, 'refs/heads/gh-readonly-queue')) + if: (matrix.os == 'ubuntu-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) uses: Pendect/action-rsyncer@v2.0.0 env: DEPLOY_KEY: ${{ secrets.buildJabRefPrivateKey }} @@ -240,13 +240,13 @@ jobs: src: 'build/distribution/' dest: jrrsync@build-upload.jabref.org:/var/www/builds.jabref.org/www/${{ steps.gitversion.outputs.branchName }}/ - name: Upload to GitHub workflow artifacts store (windows) - if: (matrix.os == 'windows-latest') && (! startsWith(github.ref, 'refs/heads/gh-readonly-queue')) + if: (matrix.os == 'windows-latest') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) uses: actions/upload-artifact@v3 with: name: JabRef-${{ matrix.displayName }} path: build/distribution - name: Upload to GitHub workflow artifacts store (macos) - if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (! startsWith(github.ref, 'refs/heads/gh-readonly-queue')) + if: (matrix.os == 'macos-latest') && (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) uses: actions/upload-artifact@v3 with: # tbn = to-be-notarized @@ -307,7 +307,7 @@ jobs: shell: bash run: ${{ matrix.archivePortable }} - name: Upload to GitHub workflow artifacts store - if: (steps.checksecrets.outputs.secretspresent == 'YES') && (! startsWith(github.ref, 'refs/heads/gh-readonly-queue')) + if: (steps.checksecrets.outputs.secretspresent == 'YES') && (!startsWith(github.ref, 'refs/heads/gh-readonly-queue')) uses: actions/upload-artifact@v3 with: name: JabRef-macOS @@ -318,7 +318,7 @@ jobs: name: Upload binaries on builds.jabref.org runs-on: ubuntu-latest needs: [build, notarize] - if: ! startsWith(github.ref, 'refs/heads/gh-readonly-queue') + if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} steps: - name: Check secrets presence id: checksecrets @@ -353,7 +353,7 @@ jobs: name: JabRef-windows path: build/distribution - name: Get macOS binaries unsigned - if: (steps.checksecrets.outputs.secretspresent == 'YES') && (inputs.notarization == false && ! startsWith(github.ref, 'refs/tags/')) + if: (steps.checksecrets.outputs.secretspresent == 'YES') && (inputs.notarization == false && !startsWith(github.ref, 'refs/tags/')) uses: actions/download-artifact@master with: name: JabRef-macOS-tbn From d743f52298d40f1ef81331dcff846b31c992667c Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Sat, 2 Sep 2023 21:33:11 +0200 Subject: [PATCH 2/4] Fix selection of table sort order (#10250) * Inline LOGGER.debug * Move out work in constructor to method * Streamline code * Same comments * Fix variable name * "Flatten" SaveOrder if OrderType.TABLE * WIP: Show diff in UI * Make it scrollable Co-authored-by: Christoph Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> * WIP: Try to fix content selector diff Co-authored-by: Christoph * Add some debugging * Update preferences immediatly after change (and not sometime later) * Fix serialization of SaveOrder * Add CHANGELOG.md entry * Fix mapping of SaveOrderConfig * Introduce SelfContainedSaveOrder * More SelfContainedSaveOrder * Remove ref to PrefsService * Compile fix * Made OrFields NOT extending LinkedHashSet * Fix hillarious bug * Fixed tests * Try to fix FieldComparators for OrFields * Fix order of null comparisons Refs tests of #7544 * Fix checkstyle * Add missing equals, hashcode and toString * Fix checkstyle * Restore test files * Fix OpenRewrite * Fix modernizer * Update CHANGELOG.md * Fix NPE --------- Co-authored-by: Christoph Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com> --- .gitignore | 2 + CHANGELOG.md | 2 + src/main/java/org/jabref/gui/JabRefFrame.java | 4 +- src/main/java/org/jabref/gui/LibraryTab.java | 6 +- .../autosaveandbackup/AutosaveManager.java | 2 +- .../autosaveandbackup/BackupManager.java | 42 ++++++++--- .../MetadataChangeDetailsView.java | 23 ++++--- .../jabref/gui/dialogs/BackupUIManager.java | 2 +- .../gui/entryeditor/RequiredFieldsTab.java | 2 +- .../gui/exporter/SaveDatabaseAction.java | 41 ++++++++--- .../importer/actions/OpenDatabaseAction.java | 2 +- .../ContentSelectorViewModel.java | 24 +++++++ .../org/jabref/gui/maintable/MainTable.java | 29 ++++---- .../gui/maintable/MainTableColumnModel.java | 19 +++++ .../MainTableFieldValueFormatter.java | 2 +- .../PersistenceVisualStateTable.java | 56 ++++++++++----- .../gui/maintable/columns/FieldColumn.java | 4 +- .../jabref/gui/search/SearchResultsTable.java | 2 +- .../jabref/logic/bibtex/BibEntryWriter.java | 12 ++-- .../bibtex/comparator/FieldComparator.java | 8 +-- .../logic/bibtex/comparator/MetaDataDiff.java | 69 ++++++++----------- .../jabref/logic/crawler/StudyRepository.java | 3 +- .../logic/exporter/BibDatabaseWriter.java | 4 +- .../logic/exporter/ExporterFactory.java | 4 +- .../logic/exporter/SaveConfiguration.java | 12 ++-- .../logic/exporter/TemplateExporter.java | 9 +-- .../style/OOBibStyleGetCitationMarker.java | 2 +- .../jabref/logic/util/io/BackupFileUtil.java | 3 +- .../model/database/event/AutosaveEvent.java | 4 +- .../java/org/jabref/model/entry/BibEntry.java | 2 +- .../model/entry/BibEntryTypeBuilder.java | 5 +- .../model/entry/field/FieldFactory.java | 2 +- .../jabref/model/entry/field/OrFields.java | 60 +++++++++++++--- .../model/metadata/ContentSelectors.java | 8 +++ .../org/jabref/model/metadata/SaveOrder.java | 20 +++--- .../metadata/SelfContainedSaveOrder.java | 27 ++++++++ .../jabref/preferences/JabRefPreferences.java | 18 ++--- .../preferences/PreferencesService.java | 3 + src/main/resources/tinylog.properties | 1 + .../BackupManagerDiscardedTest.java | 5 +- .../autosaveandbackup/BackupManagerTest.java | 15 +++- .../exporter/ExportToClipboardActionTest.java | 2 + .../comparator/FieldComparatorTest.java | 47 ++++++++++--- .../autosaveandbackup/changes.bib | 0 .../autosaveandbackup/changes.bib.bak | 0 .../autosaveandbackup/no-autosave.bib | 0 .../autosaveandbackup/no-changes.bib | 0 .../autosaveandbackup/no-changes.bib.bak | 0 48 files changed, 419 insertions(+), 190 deletions(-) rename src/main/java/org/jabref/{logic => gui}/autosaveandbackup/AutosaveManager.java (98%) rename src/main/java/org/jabref/{logic => gui}/autosaveandbackup/BackupManager.java (87%) create mode 100644 src/main/java/org/jabref/model/metadata/SelfContainedSaveOrder.java rename src/test/java/org/jabref/{logic => gui}/autosaveandbackup/BackupManagerDiscardedTest.java (95%) rename src/test/java/org/jabref/{logic => gui}/autosaveandbackup/BackupManagerTest.java (93%) rename src/test/resources/org/jabref/{logic => gui}/autosaveandbackup/changes.bib (100%) rename src/test/resources/org/jabref/{logic => gui}/autosaveandbackup/changes.bib.bak (100%) rename src/test/resources/org/jabref/{logic => gui}/autosaveandbackup/no-autosave.bib (100%) rename src/test/resources/org/jabref/{logic => gui}/autosaveandbackup/no-changes.bib (100%) rename src/test/resources/org/jabref/{logic => gui}/autosaveandbackup/no-changes.bib.bak (100%) diff --git a/.gitignore b/.gitignore index 8722502e3402..1d3864a220b6 100644 --- a/.gitignore +++ b/.gitignore @@ -478,5 +478,7 @@ lib/ojdbc.jar # do not ignore JabRef icons (they are ignored by the macos setting above) !src/main/java/org/jabref/gui/icon +!**/autosaveandbackup/*.bak + # generated during release process CHANGELOG.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 157c767ab285..6088e85301e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve ### Fixed +- It is possible again to use "current table sort order" for the order of entries when saving. [#9869](https://github.com/JabRef/jabref/issues/9869) + ### Removed ## [5.10] - 2023-09-02 diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 2458b5a41273..88a37f989279 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -54,6 +54,8 @@ import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.actions.StandardActions; +import org.jabref.gui.autosaveandbackup.AutosaveManager; +import org.jabref.gui.autosaveandbackup.BackupManager; import org.jabref.gui.auximport.NewSubLibraryAction; import org.jabref.gui.bibtexextractor.ExtractBibtexAction; import org.jabref.gui.citationkeypattern.GenerateCitationKeyAction; @@ -118,8 +120,6 @@ import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.TaskExecutor; -import org.jabref.logic.autosaveandbackup.AutosaveManager; -import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.citationstyle.CitationStyleOutputFormat; import org.jabref.logic.help.HelpFile; import org.jabref.logic.importer.IdFetcher; diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 4beabbfea928..fa02d937aa5f 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -26,6 +26,8 @@ import org.jabref.gui.autocompleter.AutoCompletePreferences; import org.jabref.gui.autocompleter.PersonNameSuggestionProvider; import org.jabref.gui.autocompleter.SuggestionProviders; +import org.jabref.gui.autosaveandbackup.AutosaveManager; +import org.jabref.gui.autosaveandbackup.BackupManager; import org.jabref.gui.collab.DatabaseChangeMonitor; import org.jabref.gui.dialogs.AutosaveUiManager; import org.jabref.gui.entryeditor.EntryEditor; @@ -39,8 +41,6 @@ import org.jabref.gui.undo.UndoableRemoveEntries; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; -import org.jabref.logic.autosaveandbackup.AutosaveManager; -import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.citationstyle.CitationStyleCache; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.importer.util.FileFieldParser; @@ -294,7 +294,7 @@ public void installAutosaveManagerAndBackupManager() { autosaveManager.registerListener(new AutosaveUiManager(this, preferencesService, entryTypesManager)); } if (isDatabaseReadyForBackup(bibDatabaseContext) && preferencesService.getFilePreferences().shouldCreateBackup()) { - BackupManager.start(bibDatabaseContext, Globals.entryTypesManager, preferencesService); + BackupManager.start(this, bibDatabaseContext, Globals.entryTypesManager, preferencesService); } } diff --git a/src/main/java/org/jabref/logic/autosaveandbackup/AutosaveManager.java b/src/main/java/org/jabref/gui/autosaveandbackup/AutosaveManager.java similarity index 98% rename from src/main/java/org/jabref/logic/autosaveandbackup/AutosaveManager.java rename to src/main/java/org/jabref/gui/autosaveandbackup/AutosaveManager.java index 990da5c2db4c..72402b18dd56 100644 --- a/src/main/java/org/jabref/logic/autosaveandbackup/AutosaveManager.java +++ b/src/main/java/org/jabref/gui/autosaveandbackup/AutosaveManager.java @@ -1,4 +1,4 @@ -package org.jabref.logic.autosaveandbackup; +package org.jabref.gui.autosaveandbackup; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java similarity index 87% rename from src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java rename to src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java index e3e5805df786..616c806891ad 100644 --- a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java +++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java @@ -1,4 +1,4 @@ -package org.jabref.logic.autosaveandbackup; +package org.jabref.gui.autosaveandbackup; import java.io.IOException; import java.io.Writer; @@ -19,6 +19,11 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import javafx.scene.control.TableColumn; + +import org.jabref.gui.LibraryTab; +import org.jabref.gui.maintable.BibEntryTableViewModel; +import org.jabref.gui.maintable.columns.MainTableColumn; import org.jabref.logic.bibtex.InvalidFieldValueException; import org.jabref.logic.exporter.AtomicFileWriter; import org.jabref.logic.exporter.BibWriter; @@ -31,6 +36,7 @@ import org.jabref.model.database.event.BibDatabaseContextChangedEvent; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.preferences.PreferencesService; import com.google.common.eventbus.Subscribe; @@ -58,17 +64,19 @@ public class BackupManager { private final ScheduledThreadPoolExecutor executor; private final CoarseChangeFilter changeFilter; private final BibEntryTypesManager entryTypesManager; + private final LibraryTab libraryTab; // Contains a list of all backup paths // During a write, the less recent backup file is deleted private final Queue backupFilesQueue = new LinkedBlockingQueue<>(); private boolean needsBackup = false; - BackupManager(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) { + BackupManager(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) { this.bibDatabaseContext = bibDatabaseContext; this.entryTypesManager = entryTypesManager; this.preferences = preferences; this.executor = new ScheduledThreadPoolExecutor(2); + this.libraryTab = libraryTab; changeFilter = new CoarseChangeFilter(bibDatabaseContext); changeFilter.registerListener(this); @@ -96,8 +104,8 @@ static Optional getLatestBackupPath(Path originalPath, Path backupDir) { * * @param bibDatabaseContext Associated {@link BibDatabaseContext} */ - public static BackupManager start(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) { - BackupManager backupManager = new BackupManager(bibDatabaseContext, entryTypesManager, preferences); + public static BackupManager start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) { + BackupManager backupManager = new BackupManager(libraryTab, bibDatabaseContext, entryTypesManager, preferences); backupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory()); runningInstances.add(backupManager); return backupManager; @@ -215,18 +223,36 @@ void performBackup(Path backupPath) { // We opted for "while" to delete backups in case there are more than 10 while (backupFilesQueue.size() >= MAXIMUM_BACKUP_FILE_COUNT) { - Path lessRecentBackupFile = backupFilesQueue.poll(); + Path oldestBackupFile = backupFilesQueue.poll(); try { - Files.delete(lessRecentBackupFile); + Files.delete(oldestBackupFile); } catch (IOException e) { - LOGGER.error("Could not delete backup file {}", lessRecentBackupFile, e); + LOGGER.error("Could not delete backup file {}", oldestBackupFile, e); } } // code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase + SelfContainedSaveOrder saveOrder = bibDatabaseContext + .getMetaData().getSaveOrder() + .map(so -> { + if (so.getOrderType() == SaveOrder.OrderType.TABLE) { + // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences + List> sortOrder = libraryTab.getMainTable().getSortOrder(); + return new SelfContainedSaveOrder( + SaveOrder.OrderType.SPECIFIED, + sortOrder.stream() + .filter(col -> col instanceof MainTableColumn) + .map(column -> ((MainTableColumn) column).getModel()) + .flatMap(model -> model.getSortCriteria().stream()) + .toList()); + } else { + return SelfContainedSaveOrder.of(so); + } + }) + .orElse(SaveOrder.getDefaultSaveOrder()); SaveConfiguration saveConfiguration = new SaveConfiguration() .withMakeBackup(false) - .withSaveOrder(bibDatabaseContext.getMetaData().getSaveOrder().orElse(SaveOrder.getDefaultSaveOrder())) + .withSaveOrder(saveOrder) .withReformatOnSave(preferences.getLibraryPreferences().shouldAlwaysReformatOnSave()); Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8); diff --git a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java index cddcdf32e2d4..3c2a81e3e01e 100644 --- a/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java +++ b/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java @@ -1,6 +1,7 @@ package org.jabref.gui.collab.metedatachange; import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; import javafx.scene.layout.VBox; import org.jabref.gui.collab.DatabaseChangeDetailsView; @@ -17,20 +18,24 @@ public MetadataChangeDetailsView(MetadataChange metadataChange, PreferencesServi header.getStyleClass().add("sectionHeader"); container.getChildren().add(header); - for (MetaDataDiff.Difference change : metadataChange.getMetaDataDiff().getDifferences(preferencesService)) { - container.getChildren().add(new Label(getDifferenceString(change))); + for (MetaDataDiff.Difference diff : metadataChange.getMetaDataDiff().getDifferences(preferencesService)) { + container.getChildren().add(new Label(getDifferenceString(diff.differenceType()))); + container.getChildren().add(new Label(diff.originalObject().toString())); + container.getChildren().add(new Label(diff.newObject().toString())); } - setLeftAnchor(container, 8d); - setTopAnchor(container, 8d); - setRightAnchor(container, 8d); - setBottomAnchor(container, 8d); + ScrollPane scrollPane = new ScrollPane(container); + scrollPane.setFitToWidth(true); + getChildren().setAll(scrollPane); - getChildren().setAll(container); + setLeftAnchor(scrollPane, 8d); + setTopAnchor(scrollPane, 8d); + setRightAnchor(scrollPane, 8d); + setBottomAnchor(scrollPane, 8d); } - private String getDifferenceString(MetaDataDiff.Difference change) { - return switch (change) { + private String getDifferenceString(MetaDataDiff.DifferenceType changeType) { + return switch (changeType) { case PROTECTED -> Localization.lang("Library protection"); case GROUPS_ALTERED -> diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java index e416615ad235..05ba9755db4f 100644 --- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java +++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java @@ -8,13 +8,13 @@ import javafx.scene.control.ButtonType; import org.jabref.gui.DialogService; +import org.jabref.gui.autosaveandbackup.BackupManager; import org.jabref.gui.backup.BackupResolverDialog; import org.jabref.gui.collab.DatabaseChange; import org.jabref.gui.collab.DatabaseChangeList; import org.jabref.gui.collab.DatabaseChangeResolverFactory; import org.jabref.gui.collab.DatabaseChangesResolverDialog; import org.jabref.gui.util.DefaultTaskExecutor; -import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.OpenDatabase; import org.jabref.logic.importer.ParserResult; diff --git a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java index 9034a76f0c49..cfc530c9d085 100644 --- a/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/RequiredFieldsTab.java @@ -56,7 +56,7 @@ protected Set determineFieldsToShow(BibEntry entry) { Set fields = new LinkedHashSet<>(); if (entryType.isPresent()) { for (OrFields orFields : entryType.get().getRequiredFields()) { - fields.addAll(orFields); + fields.addAll(orFields.getFields()); } // Add the edit field for Bibtex-key. fields.add(InternalField.KEY_FIELD); diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java index f56852c25a8b..096646fd267d 100644 --- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java +++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java @@ -6,6 +6,7 @@ import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -13,16 +14,19 @@ import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; import javafx.scene.control.DialogPane; +import javafx.scene.control.TableColumn; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import org.jabref.gui.DialogService; import org.jabref.gui.JabRefFrame; import org.jabref.gui.LibraryTab; +import org.jabref.gui.autosaveandbackup.AutosaveManager; +import org.jabref.gui.autosaveandbackup.BackupManager; +import org.jabref.gui.maintable.BibEntryTableViewModel; +import org.jabref.gui.maintable.columns.MainTableColumn; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.FileDialogConfiguration; -import org.jabref.logic.autosaveandbackup.AutosaveManager; -import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.exporter.AtomicFileWriter; import org.jabref.logic.exporter.BibDatabaseWriter; import org.jabref.logic.exporter.BibWriter; @@ -38,6 +42,7 @@ import org.jabref.model.database.event.ChangePropagation; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.preferences.PreferencesService; import org.slf4j.Logger; @@ -90,11 +95,31 @@ public boolean saveAs(Path file) { return this.saveAs(file, SaveDatabaseMode.NORMAL); } + private SelfContainedSaveOrder getSaveOrder() { + return libraryTab.getBibDatabaseContext() + .getMetaData().getSaveOrder() + .map(so -> { + if (so.getOrderType() == SaveOrder.OrderType.TABLE) { + // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences + List> sortOrder = libraryTab.getMainTable().getSortOrder(); + return new SelfContainedSaveOrder( + SaveOrder.OrderType.SPECIFIED, + sortOrder.stream() + .filter(col -> col instanceof MainTableColumn) + .map(column -> ((MainTableColumn) column).getModel()) + .flatMap(model -> model.getSortCriteria().stream()) + .toList()); + } else { + return SelfContainedSaveOrder.of(so); + } + }) + .orElse(SaveOrder.getDefaultSaveOrder()); + } + public void saveSelectedAsPlain() { askForSavePath().ifPresent(path -> { try { - saveDatabase(path, true, StandardCharsets.UTF_8, BibDatabaseWriter.SaveType.PLAIN_BIBTEX, - libraryTab.getBibDatabaseContext().getMetaData().getSaveOrder().orElse(SaveOrder.getDefaultSaveOrder())); + saveDatabase(path, true, StandardCharsets.UTF_8, BibDatabaseWriter.SaveType.PLAIN_BIBTEX, getSaveOrder()); frame.getFileHistory().newFile(path); dialogService.notify(Localization.lang("Saved selected to '%0'.", path.toString())); } catch (SaveException ex) { @@ -211,9 +236,7 @@ private boolean save(Path targetPath, SaveDatabaseMode mode) { // Make sure to remember which encoding we used libraryTab.getBibDatabaseContext().getMetaData().setEncoding(encoding, ChangePropagation.DO_NOT_POST_EVENT); - // Save the database - boolean success = saveDatabase(targetPath, false, encoding, BibDatabaseWriter.SaveType.WITH_JABREF_META_DATA, - libraryTab.getBibDatabaseContext().getMetaData().getSaveOrder().orElse(SaveOrder.getDefaultSaveOrder())); + boolean success = saveDatabase(targetPath, false, encoding, BibDatabaseWriter.SaveType.WITH_JABREF_META_DATA, getSaveOrder()); if (success) { libraryTab.getUndoManager().markUnchanged(); @@ -231,7 +254,7 @@ private boolean save(Path targetPath, SaveDatabaseMode mode) { } } - private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, BibDatabaseWriter.SaveType saveType, SaveOrder saveOrder) throws SaveException { + private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, BibDatabaseWriter.SaveType saveType, SelfContainedSaveOrder saveOrder) throws SaveException { // if this code is adapted, please also adapt org.jabref.logic.autosaveandbackup.BackupManager.performBackup SaveConfiguration saveConfiguration = new SaveConfiguration() @@ -269,7 +292,7 @@ private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, } } - private void saveWithDifferentEncoding(Path file, boolean selectedOnly, Charset encoding, Set encodingProblems, BibDatabaseWriter.SaveType saveType, SaveOrder saveOrder) throws SaveException { + private void saveWithDifferentEncoding(Path file, boolean selectedOnly, Charset encoding, Set encodingProblems, BibDatabaseWriter.SaveType saveType, SelfContainedSaveOrder saveOrder) throws SaveException { DialogPane pane = new DialogPane(); VBox vbox = new VBox(); vbox.getChildren().addAll( diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java index 1778074a4449..121cf109c2e5 100644 --- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java +++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java @@ -17,13 +17,13 @@ import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.autosaveandbackup.BackupManager; import org.jabref.gui.dialogs.BackupUIManager; import org.jabref.gui.menus.FileHistoryMenu; import org.jabref.gui.shared.SharedDatabaseUIManager; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.FileDialogConfiguration; -import org.jabref.logic.autosaveandbackup.BackupManager; import org.jabref.logic.importer.OpenDatabase; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; diff --git a/src/main/java/org/jabref/gui/libraryproperties/contentselectors/ContentSelectorViewModel.java b/src/main/java/org/jabref/gui/libraryproperties/contentselectors/ContentSelectorViewModel.java index a397170c92ba..db23506e8fd2 100644 --- a/src/main/java/org/jabref/gui/libraryproperties/contentselectors/ContentSelectorViewModel.java +++ b/src/main/java/org/jabref/gui/libraryproperties/contentselectors/ContentSelectorViewModel.java @@ -5,6 +5,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -67,12 +68,35 @@ public void setValues() { @Override public void storeSettings() { List metaDataFields = metaData.getContentSelectors().getFieldsWithSelectors(); + + if (isDefaultMap(fieldKeywordsMap)) { + Iterator iterator = metaData.getContentSelectors().getContentSelectors().iterator(); + while (iterator.hasNext()) { + metaData.clearContentSelectors(iterator.next().getField()); + } + } + fieldKeywordsMap.forEach((field, keywords) -> updateMetaDataContentSelector(metaDataFields, field, keywords)); List fieldNamesToRemove = filterFieldsToRemove(); fieldNamesToRemove.forEach(metaData::clearContentSelectors); } + private boolean isDefaultMap(Map> fieldKeywordsMap) { + if (fieldKeywordsMap.size() != DEFAULT_FIELD_NAMES.size()) { + return false; + } + for (Field field : DEFAULT_FIELD_NAMES) { + if (!fieldKeywordsMap.containsKey(field)) { + return false; + } + if (!fieldKeywordsMap.get(field).isEmpty()) { + return false; + } + } + return true; + } + public ListProperty getFieldNamesBackingList() { return fields; } diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index ee5242099cae..10f9f68022e0 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -148,25 +148,15 @@ public MainTable(MainTableDataModel model, this.getSortOrder().clear(); - /* KEEP for debugging purposes - for (var colModel : mainTablePreferences.getColumnPreferences().getColumnSortOrder()) { - for (var col : this.getColumns()) { - var tablecColModel = ((MainTableColumn) col).getModel(); - if (tablecColModel.equals(colModel)) { - LOGGER.debug("Adding sort order for col {} ", col); - this.getSortOrder().add(col); - break; - } - } - } - */ - mainTablePreferences.getColumnPreferences().getColumnSortOrder().forEach(columnModel -> this.getColumns().stream() .map(column -> (MainTableColumn) column) .filter(column -> column.getModel().equals(columnModel)) .findFirst() - .ifPresent(column -> this.getSortOrder().add(column))); + .ifPresent(column -> { + LOGGER.debug("Adding sort order for col {} ", column); + this.getSortOrder().add(column); + })); if (mainTablePreferences.getResizeColumnsToFit()) { this.setColumnResizePolicy(new SmartConstrainedResizePolicy()); @@ -182,7 +172,7 @@ public MainTable(MainTableDataModel model, this.getStylesheets().add(MainTable.class.getResource("MainTable.css").toExternalForm()); // Store visual state - new PersistenceVisualStateTable(this, mainTablePreferences.getColumnPreferences()); + new PersistenceVisualStateTable(this, mainTablePreferences.getColumnPreferences()).addListeners(); setupKeyBindings(keyBindingRepository); @@ -202,6 +192,7 @@ public MainTable(MainTableDataModel model, libraryTab.getUndoManager(), dialogService, stateManager); + // Enable the header right-click menu. new MainTableHeaderContextMenu(this, rightClickMenuFactory).show(true); } @@ -212,7 +203,6 @@ public MainTable(MainTableDataModel model, * @param sortedColumn The sorted column in {@link MainTable} * @param keyEvent The pressed character */ - private void jumpToSearchKey(TableColumn sortedColumn, KeyEvent keyEvent) { if ((keyEvent.getCharacter() == null) || (sortedColumn == null)) { return; @@ -494,4 +484,11 @@ private Optional findEntry(BibEntry entry) { .filter(viewModel -> viewModel.getEntry().equals(entry)) .findFirst(); } + + public static List toColumnModels(List> columns) { + return columns.stream() + .filter(col -> col instanceof MainTableColumn) + .map(column -> ((MainTableColumn) column).getModel()) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/org/jabref/gui/maintable/MainTableColumnModel.java b/src/main/java/org/jabref/gui/maintable/MainTableColumnModel.java index 5ff872c14260..77bcecaf7a1a 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableColumnModel.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableColumnModel.java @@ -1,6 +1,7 @@ package org.jabref.gui.maintable; import java.util.EnumSet; +import java.util.List; import java.util.Objects; import javafx.beans.property.DoubleProperty; @@ -15,6 +16,7 @@ import org.jabref.gui.util.FieldsUtil; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.field.FieldFactory; +import org.jabref.model.metadata.SaveOrder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -142,6 +144,8 @@ public String getDisplayName() { || (typeProperty.getValue() == Type.INDEX)) { return typeProperty.getValue().getDisplayName(); } else { + // In case an OrField is used, `FieldFactory.parseField` returns UnknownField, which leads to + // "author/editor(Custom)" instead of "author/editor" in the output return FieldsUtil.getNameWithType(FieldFactory.parseField(qualifierProperty.getValue())); } } @@ -166,6 +170,21 @@ public ObjectProperty sortTypeProperty() { return sortTypeProperty; } + /** + * Returns a list of sort cirteria based on the fields the current column displays. + * In case it is single field, a single SortCriterion is returned. + * In case of multiple fields, for each field, there is a SortCriterion contained in the list. + * + * Implementation reason: We want to have SortCriterion handle a single field, because the UI allows for handling + * "plain" fields only. + */ + public List getSortCriteria() { + boolean descending = getSortType() == TableColumn.SortType.DESCENDING; + return FieldFactory.parseOrFields(getQualifier()).getFields().stream() + .map(field -> new SaveOrder.SortCriterion(field, descending)) + .toList(); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/org/jabref/gui/maintable/MainTableFieldValueFormatter.java b/src/main/java/org/jabref/gui/maintable/MainTableFieldValueFormatter.java index bb04a6775f43..33841527a34f 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTableFieldValueFormatter.java +++ b/src/main/java/org/jabref/gui/maintable/MainTableFieldValueFormatter.java @@ -33,7 +33,7 @@ public MainTableFieldValueFormatter(NameDisplayPreferences nameDisplayPreference * @return The formatted name field. */ public String formatFieldsValues(final OrFields fields, final BibEntry entry) { - for (Field field : fields) { + for (Field field : fields.getFields()) { if (field.getProperties().contains(FieldProperty.PERSON_NAMES) && (displayStyle != DisplayStyle.AS_IS)) { Optional name = entry.getResolvedFieldOrAlias(field, bibDatabase); diff --git a/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java b/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java index eed6c6081241..e5b1178f1a00 100644 --- a/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java +++ b/src/main/java/org/jabref/gui/maintable/PersistenceVisualStateTable.java @@ -1,55 +1,73 @@ package org.jabref.gui.maintable; +import java.util.List; import java.util.stream.Collectors; import javafx.beans.InvalidationListener; +import javafx.collections.ListChangeListener; +import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import org.jabref.gui.maintable.columns.MainTableColumn; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Keep track of changes made to the columns (reordering, resorting, resizing). */ public class PersistenceVisualStateTable { + private static final Logger LOGGER = LoggerFactory.getLogger(PersistenceVisualStateTable.class); + protected final TableView table; protected final ColumnPreferences preferences; - public PersistenceVisualStateTable(final TableView table, ColumnPreferences preferences) { + public PersistenceVisualStateTable(TableView table, ColumnPreferences preferences) { this.table = table; this.preferences = preferences; + } + public void addListeners() { table.getColumns().addListener((InvalidationListener) obs -> updateColumns()); - table.getSortOrder().addListener((InvalidationListener) obs -> updateSortOrder()); + table.getSortOrder().addListener((ListChangeListener>) obs -> updateSortOrder()); // As we store the ColumnModels of the MainTable, we need to add the listener to the ColumnModel properties, // since the value is bound to the model after the listener to the column itself is called. - table.getColumns().forEach(col -> - ((MainTableColumn) col).getModel().widthProperty().addListener(obs -> updateColumns())); - table.getColumns().forEach(col -> - ((MainTableColumn) col).getModel().sortTypeProperty().addListener(obs -> updateColumns())); + + table.getColumns().stream() + .map(col -> ((MainTableColumn) col).getModel()) + .forEach(model -> { + model.widthProperty().addListener(obs -> updateColumns()); + model.sortTypeProperty().addListener(obs -> updateColumns()); + }); } /** - * Stores shown columns, their width and their sortType in preferences. + * Stores shown columns, their width and their {@link TableColumn.SortType} in preferences. + * The conversion to the "real" string in the preferences is made at + * {@link org.jabref.preferences.JabRefPreferences#getColumnSortTypesAsStringList(ColumnPreferences)} */ private void updateColumns() { - preferences.setColumns( - table.getColumns().stream() - .filter(col -> col instanceof MainTableColumn) - .map(column -> ((MainTableColumn) column).getModel()) - .collect(Collectors.toList())); + LOGGER.debug("Updating columns"); + preferences.setColumns(toList(table.getColumns())); } /** - * Stores the SortOrder of the Table in the preferences. Cannot be combined with updateColumns, because JavaFX - * would provide just an empty list for the sort order on other changes. + * Stores the SortOrder of the Table in the preferences. This includes {@link TableColumn.SortType}. + *
+ * Cannot be combined with updateColumns, because JavaFX would provide just an empty list for the sort order + * on other changes. */ private void updateSortOrder() { - preferences.setColumnSortOrder( - table.getSortOrder().stream() - .filter(col -> col instanceof MainTableColumn) - .map(column -> ((MainTableColumn) column).getModel()) - .collect(Collectors.toList())); + LOGGER.debug("Updating sort order"); + preferences.setColumnSortOrder(toList(table.getSortOrder())); + } + + private List toList(List> columns) { + return columns.stream() + .filter(col -> col instanceof MainTableColumn) + .map(column -> ((MainTableColumn) column).getModel()) + .collect(Collectors.toList()); } } diff --git a/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java b/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java index e1b8b1c1ff9e..698e8e168953 100644 --- a/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java +++ b/src/main/java/org/jabref/gui/maintable/columns/FieldColumn.java @@ -31,9 +31,9 @@ public FieldColumn(MainTableColumnModel model) { .withText(text -> text) .install(this); - if (fields.size() == 1) { + if (fields.hasExactlyOne()) { // comparator can't parse more than one value - Field field = fields.stream().collect(MoreCollectors.onlyElement()); + Field field = fields.getFields().stream().collect(MoreCollectors.onlyElement()); if ((field instanceof UnknownField) || field.isNumeric()) { this.setComparator(new NumericFieldComparator()); diff --git a/src/main/java/org/jabref/gui/search/SearchResultsTable.java b/src/main/java/org/jabref/gui/search/SearchResultsTable.java index bfe8aa3d84d3..5d839d2a4a1e 100644 --- a/src/main/java/org/jabref/gui/search/SearchResultsTable.java +++ b/src/main/java/org/jabref/gui/search/SearchResultsTable.java @@ -66,7 +66,7 @@ public SearchResultsTable(SearchResultsTableDataModel model, this.getStylesheets().add(MainTable.class.getResource("MainTable.css").toExternalForm()); // Store visual state - new PersistenceVisualStateTable(this, preferencesService.getSearchDialogColumnPreferences()); + new PersistenceVisualStateTable(this, preferencesService.getSearchDialogColumnPreferences()).addListeners(); database.getDatabase().registerListener(this); } diff --git a/src/main/java/org/jabref/logic/bibtex/BibEntryWriter.java b/src/main/java/org/jabref/logic/bibtex/BibEntryWriter.java index 7cec02d2a491..663fa3e3fee6 100644 --- a/src/main/java/org/jabref/logic/bibtex/BibEntryWriter.java +++ b/src/main/java/org/jabref/logic/bibtex/BibEntryWriter.java @@ -24,6 +24,7 @@ import org.jabref.model.entry.field.BibField; import org.jabref.model.entry.field.Field; import org.jabref.model.entry.field.InternalField; +import org.jabref.model.entry.field.OrFields; import org.jabref.model.strings.StringUtil; import org.slf4j.LoggerFactory; @@ -103,11 +104,12 @@ private void writeRequiredFieldsFirstRemainingFieldsSecond(BibEntry entry, BibWr if (type.isPresent()) { // Write required fields first List requiredFields = type.get() - .getRequiredFields() - .stream() - .flatMap(Collection::stream) - .sorted(Comparator.comparing(Field::getName)) - .collect(Collectors.toList()); + .getRequiredFields() + .stream() + .map(OrFields::getFields) + .flatMap(Collection::stream) + .sorted(Comparator.comparing(Field::getName)) + .collect(Collectors.toList()); for (Field field : requiredFields) { writeField(entry, out, field, indent); diff --git a/src/main/java/org/jabref/logic/bibtex/comparator/FieldComparator.java b/src/main/java/org/jabref/logic/bibtex/comparator/FieldComparator.java index d8d897eb1d32..42f86e0d78d2 100644 --- a/src/main/java/org/jabref/logic/bibtex/comparator/FieldComparator.java +++ b/src/main/java/org/jabref/logic/bibtex/comparator/FieldComparator.java @@ -73,7 +73,7 @@ private FieldType determineFieldType() { } private String getFieldValue(BibEntry entry) { - for (Field aField : fields) { + for (Field aField : fields.getFields()) { Optional o = entry.getFieldOrAliasLatexFree(aField); if (o.isPresent()) { return o.get(); @@ -92,8 +92,6 @@ public int compare(BibEntry e1, BibEntry e2) { f1 = e1.getType().getDisplayName(); f2 = e2.getType().getDisplayName(); } else { - // If the field is author or editor, we rearrange names so they are - // sorted according to last name. f1 = getFieldValue(e1); f2 = getFieldValue(e2); } @@ -102,9 +100,9 @@ public int compare(BibEntry e1, BibEntry e2) { if ((f1 == null) && (f2 == null)) { return 0; } else if (f1 == null) { - return multiplier; - } else if (f2 == null) { return -multiplier; + } else if (f2 == null) { + return +multiplier; } // Now we know that both f1 and f2 are != null diff --git a/src/main/java/org/jabref/logic/bibtex/comparator/MetaDataDiff.java b/src/main/java/org/jabref/logic/bibtex/comparator/MetaDataDiff.java index a18058625084..5420ef97391d 100644 --- a/src/main/java/org/jabref/logic/bibtex/comparator/MetaDataDiff.java +++ b/src/main/java/org/jabref/logic/bibtex/comparator/MetaDataDiff.java @@ -1,6 +1,7 @@ package org.jabref.logic.bibtex.comparator; -import java.util.EnumSet; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -8,7 +9,7 @@ import org.jabref.preferences.PreferencesService; public class MetaDataDiff { - public enum Difference { + public enum DifferenceType { PROTECTED, GROUPS_ALTERED, ENCODING, @@ -23,6 +24,9 @@ public enum Difference { CONTENT_SELECTOR } + public record Difference(DifferenceType differenceType, Object originalObject, Object newObject) { + } + private final Optional groupDiff; private final MetaData originalMetaData; private final MetaData newMetaData; @@ -41,50 +45,31 @@ public static Optional compare(MetaData originalMetaData, MetaData } } + private void addToListIfDiff(List changes, DifferenceType differenceType, Object originalObject, Object newObject) { + if (!Objects.equals(originalObject, newObject)) { + changes.add(new Difference(differenceType, originalObject, newObject)); + } + } + /** * Should be kept in sync with {@link MetaData#equals(Object)} */ - public EnumSet getDifferences(PreferencesService preferences) { - EnumSet changes = EnumSet.noneOf(Difference.class); - - if (originalMetaData.isProtected() != newMetaData.isProtected()) { - changes.add(Difference.PROTECTED); - } - if (!Objects.equals(originalMetaData.getGroups(), newMetaData.getGroups())) { - changes.add(Difference.GROUPS_ALTERED); - } - if (!Objects.equals(originalMetaData.getEncoding(), newMetaData.getEncoding())) { - changes.add(Difference.ENCODING); - } - if (!Objects.equals(originalMetaData.getSaveOrder(), newMetaData.getSaveOrder())) { - changes.add(Difference.SAVE_SORT_ORDER); - } - if (!Objects.equals( + public List getDifferences(PreferencesService preferences) { + List changes = new ArrayList<>(); + addToListIfDiff(changes, DifferenceType.PROTECTED, originalMetaData.isProtected(), newMetaData.isProtected()); + addToListIfDiff(changes, DifferenceType.GROUPS_ALTERED, originalMetaData.getGroups(), newMetaData.getGroups()); + addToListIfDiff(changes, DifferenceType.ENCODING, originalMetaData.getEncoding(), newMetaData.getEncoding()); + addToListIfDiff(changes, DifferenceType.SAVE_SORT_ORDER, originalMetaData.getSaveOrder(), newMetaData.getSaveOrder()); + addToListIfDiff(changes, DifferenceType.KEY_PATTERNS, originalMetaData.getCiteKeyPattern(preferences.getCitationKeyPatternPreferences().getKeyPattern()), - newMetaData.getCiteKeyPattern(preferences.getCitationKeyPatternPreferences().getKeyPattern()))) { - changes.add(Difference.KEY_PATTERNS); - } - if (!Objects.equals(originalMetaData.getUserFileDirectories(), newMetaData.getUserFileDirectories())) { - changes.add(Difference.USER_FILE_DIRECTORY); - } - if (!Objects.equals(originalMetaData.getLatexFileDirectories(), newMetaData.getLatexFileDirectories())) { - changes.add(Difference.LATEX_FILE_DIRECTORY); - } - if (!Objects.equals(originalMetaData.getDefaultCiteKeyPattern(), newMetaData.getDefaultCiteKeyPattern())) { - changes.add(Difference.DEFAULT_KEY_PATTERN); - } - if (!Objects.equals(originalMetaData.getSaveActions(), newMetaData.getSaveActions())) { - changes.add(Difference.SAVE_ACTIONS); - } - if (!originalMetaData.getMode().equals(newMetaData.getMode())) { - changes.add(Difference.MODE); - } - if (!Objects.equals(originalMetaData.getDefaultFileDirectory(), newMetaData.getDefaultFileDirectory())) { - changes.add(Difference.GENERAL_FILE_DIRECTORY); - } - if (!Objects.equals(originalMetaData.getContentSelectors(), newMetaData.getContentSelectors())) { - changes.add(Difference.CONTENT_SELECTOR); - } + newMetaData.getCiteKeyPattern(preferences.getCitationKeyPatternPreferences().getKeyPattern())); + addToListIfDiff(changes, DifferenceType.USER_FILE_DIRECTORY, originalMetaData.getUserFileDirectories(), newMetaData.getUserFileDirectories()); + addToListIfDiff(changes, DifferenceType.LATEX_FILE_DIRECTORY, originalMetaData.getLatexFileDirectories(), newMetaData.getLatexFileDirectories()); + addToListIfDiff(changes, DifferenceType.DEFAULT_KEY_PATTERN, originalMetaData.getDefaultCiteKeyPattern(), newMetaData.getDefaultCiteKeyPattern()); + addToListIfDiff(changes, DifferenceType.SAVE_ACTIONS, originalMetaData.getSaveActions(), newMetaData.getSaveActions()); + addToListIfDiff(changes, DifferenceType.MODE, originalMetaData.getMode(), newMetaData.getMode()); + addToListIfDiff(changes, DifferenceType.GENERAL_FILE_DIRECTORY, originalMetaData.getDefaultFileDirectory(), newMetaData.getDefaultFileDirectory()); + addToListIfDiff(changes, DifferenceType.CONTENT_SELECTOR, originalMetaData.getContentSelectors(), newMetaData.getContentSelectors()); return changes; } diff --git a/src/main/java/org/jabref/logic/crawler/StudyRepository.java b/src/main/java/org/jabref/logic/crawler/StudyRepository.java index 0911fa3d943a..9e3abbe08049 100644 --- a/src/main/java/org/jabref/logic/crawler/StudyRepository.java +++ b/src/main/java/org/jabref/logic/crawler/StudyRepository.java @@ -28,6 +28,7 @@ import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntryTypesManager; import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.model.study.FetchResult; import org.jabref.model.study.QueryResult; import org.jabref.model.study.Study; @@ -427,7 +428,7 @@ private void generateCiteKeys(BibDatabaseContext existingEntries, BibDatabase ta private void writeResultToFile(Path pathToFile, BibDatabaseContext context) throws SaveException { try (AtomicFileWriter fileWriter = new AtomicFileWriter(pathToFile, StandardCharsets.UTF_8)) { SaveConfiguration saveConfiguration = new SaveConfiguration() - .withSaveOrder(context.getMetaData().getSaveOrder().orElse(SaveOrder.getDefaultSaveOrder())) + .withSaveOrder(context.getMetaData().getSaveOrder().map(so -> SelfContainedSaveOrder.of(so)).orElse(SaveOrder.getDefaultSaveOrder())) .withReformatOnSave(preferencesService.getLibraryPreferences().shouldAlwaysReformatOnSave()); BibWriter bibWriter = new BibWriter(fileWriter, OS.NEWLINE); BibtexDatabaseWriter databaseWriter = new BibtexDatabaseWriter( diff --git a/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java b/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java index 80be19dcc575..e8c417b157da 100644 --- a/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java +++ b/src/main/java/org/jabref/logic/exporter/BibDatabaseWriter.java @@ -39,6 +39,7 @@ import org.jabref.model.entry.field.InternalField; import org.jabref.model.metadata.MetaData; import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.model.strings.StringUtil; import org.jooq.lambda.Unchecked; @@ -70,6 +71,7 @@ public BibDatabaseWriter(BibWriter bibWriter, this.saveConfiguration = saveConfiguration; this.keyPatternPreferences = keyPatternPreferences; this.entryTypesManager = entryTypesManager; + assert saveConfiguration.getSaveOrder().getOrderType() != SaveOrder.OrderType.TABLE; } private static List applySaveActions(List toChange, MetaData metaData) { @@ -128,7 +130,7 @@ private static List> getSaveComparators(SaveOrder saveOrder * non-database save operation (such as the exportDatabase call), we do not wish to use the global preference of * saving in standard order. */ - public static List getSortedEntries(List entriesToSort, SaveOrder saveOrder) { + public static List getSortedEntries(List entriesToSort, SelfContainedSaveOrder saveOrder) { Objects.requireNonNull(entriesToSort); Objects.requireNonNull(saveOrder); diff --git a/src/main/java/org/jabref/logic/exporter/ExporterFactory.java b/src/main/java/org/jabref/logic/exporter/ExporterFactory.java index 59603deaa2a1..78373ef98d49 100644 --- a/src/main/java/org/jabref/logic/exporter/ExporterFactory.java +++ b/src/main/java/org/jabref/logic/exporter/ExporterFactory.java @@ -13,7 +13,7 @@ import org.jabref.logic.xmp.XmpPreferences; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntryTypesManager; -import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.preferences.PreferencesService; public class ExporterFactory { @@ -29,7 +29,7 @@ public static ExporterFactory create(PreferencesService preferencesService, List customFormats = preferencesService.getExportPreferences().getCustomExporters(); LayoutFormatterPreferences layoutPreferences = preferencesService.getLayoutFormatterPreferences(); - SaveOrder saveOrder = preferencesService.getExportConfiguration().getSaveOrder(); + SelfContainedSaveOrder saveOrder = SelfContainedSaveOrder.of(preferencesService.getExportConfiguration().getSaveOrder()); XmpPreferences xmpPreferences = preferencesService.getXmpPreferences(); FieldPreferences fieldPreferences = preferencesService.getFieldPreferences(); BibDatabaseMode bibDatabaseMode = preferencesService.getLibraryPreferences().getDefaultBibDatabaseMode(); diff --git a/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java b/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java index 9a93467390f8..29742fd4a586 100644 --- a/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java +++ b/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java @@ -1,6 +1,8 @@ package org.jabref.logic.exporter; +import org.jabref.gui.autosaveandbackup.BackupManager; import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; public class SaveConfiguration { @@ -8,11 +10,11 @@ public class SaveConfiguration { public static final String ENCODING_PREFIX = "Encoding: "; private boolean reformatFile; - private SaveOrder saveOrder; + private SelfContainedSaveOrder saveOrder; private boolean makeBackup; private BibDatabaseWriter.SaveType saveType; - public SaveConfiguration(SaveOrder saveOrder, + public SaveConfiguration(SelfContainedSaveOrder saveOrder, Boolean makeBackup, BibDatabaseWriter.SaveType saveType, Boolean reformatFile) { @@ -29,11 +31,11 @@ public SaveConfiguration() { false); } - public SaveOrder getSaveOrder() { + public SelfContainedSaveOrder getSaveOrder() { return saveOrder; } - public SaveConfiguration withSaveOrder(SaveOrder newSaveOrder) { + public SaveConfiguration withSaveOrder(SelfContainedSaveOrder newSaveOrder) { this.saveOrder = newSaveOrder; return this; } @@ -43,7 +45,7 @@ public boolean shouldMakeBackup() { } /** - * Required by {@link org.jabref.logic.autosaveandbackup.BackupManager}. Should not be used in other settings + * Required by {@link BackupManager}. Should not be used in other settings * * @param newMakeBackup whether a backup (.bak file) should be made */ diff --git a/src/main/java/org/jabref/logic/exporter/TemplateExporter.java b/src/main/java/org/jabref/logic/exporter/TemplateExporter.java index 178171837040..c2e0e7e6e1c3 100644 --- a/src/main/java/org/jabref/logic/exporter/TemplateExporter.java +++ b/src/main/java/org/jabref/logic/exporter/TemplateExporter.java @@ -29,6 +29,7 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.types.EntryType; import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +51,7 @@ public class TemplateExporter extends Exporter { private final String lfFileName; private final String directory; private final LayoutFormatterPreferences layoutPreferences; - private final SaveOrder saveOrder; + private final SelfContainedSaveOrder saveOrder; private boolean customExport; private BlankLineBehaviour blankLineBehaviour; @@ -82,7 +83,7 @@ public TemplateExporter(String name, String lfFileName, String extension, LayoutFormatterPreferences layoutPreferences, - SaveOrder saveOrder) { + SelfContainedSaveOrder saveOrder) { this(name, name, lfFileName, @@ -107,7 +108,7 @@ public TemplateExporter(String displayName, String directory, FileType extension, LayoutFormatterPreferences layoutPreferences, - SaveOrder saveOrder) { + SelfContainedSaveOrder saveOrder) { this(displayName, consoleName, lfFileName, directory, extension, layoutPreferences, saveOrder, null); } @@ -128,7 +129,7 @@ public TemplateExporter(String displayName, String directory, FileType extension, LayoutFormatterPreferences layoutPreferences, - SaveOrder saveOrder, + SelfContainedSaveOrder saveOrder, BlankLineBehaviour blankLineBehaviour) { super(consoleName, displayName, extension); if (Objects.requireNonNull(lfFileName).endsWith(LAYOUT_EXTENSION)) { 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 b3680b57b9b3..278b05d95951 100644 --- a/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java +++ b/src/main/java/org/jabref/logic/openoffice/style/OOBibStyleGetCitationMarker.java @@ -230,7 +230,7 @@ private static Optional getRawCitationMarkerField(BibEntry entr Objects.requireNonNull(entry, "Entry cannot be null"); Objects.requireNonNull(database, "database cannot be null"); - for (Field field : fields /* FieldFactory.parseOrFields(fields)*/) { + for (Field field : fields.getFields() /* FieldFactory.parseOrFields(fields)*/) { Optional optionalContent = entry.getResolvedFieldOrAlias(field, database); final boolean foundSomething = optionalContent.isPresent() && !optionalContent.get().trim().isEmpty(); diff --git a/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java b/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java index 0e499b5c51e8..bea88213b6f6 100644 --- a/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java +++ b/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java @@ -9,6 +9,7 @@ import java.util.HexFormat; import java.util.Optional; +import org.jabref.gui.autosaveandbackup.BackupManager; import org.jabref.logic.util.BackupFileType; import org.slf4j.Logger; @@ -33,7 +34,7 @@ private BackupFileUtil() { * In case that fails, the return path of the .bak file is set to be next to the .bib file *

*

- * Note that this backup is different from the .sav file generated by {@link org.jabref.logic.autosaveandbackup.BackupManager} + * Note that this backup is different from the .sav file generated by {@link BackupManager} * (and configured in the preferences as "make backups") *

*/ diff --git a/src/main/java/org/jabref/model/database/event/AutosaveEvent.java b/src/main/java/org/jabref/model/database/event/AutosaveEvent.java index b89ffe7d8c46..dbfd66d98580 100644 --- a/src/main/java/org/jabref/model/database/event/AutosaveEvent.java +++ b/src/main/java/org/jabref/model/database/event/AutosaveEvent.java @@ -1,7 +1,9 @@ package org.jabref.model.database.event; +import org.jabref.gui.autosaveandbackup.AutosaveManager; + /** - * This Event is fired from {@link org.jabref.logic.autosaveandbackup.AutosaveManager} in case that a save task is pending. + * This Event is fired from {@link AutosaveManager} in case that a save task is pending. */ public class AutosaveEvent { // no data diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java index 3f6b82ae6b4a..7fdd429a5976 100644 --- a/src/main/java/org/jabref/model/entry/BibEntry.java +++ b/src/main/java/org/jabref/model/entry/BibEntry.java @@ -158,7 +158,7 @@ public Optional setMonth(Month parsedMonth) { } public Optional getResolvedFieldOrAlias(OrFields fields, BibDatabase database) { - for (Field field : fields) { + for (Field field : fields.getFields()) { Optional value = getResolvedFieldOrAlias(field, database); if (value.isPresent()) { return value; diff --git a/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java b/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java index ddc510d7d4bc..b8613157ac6e 100644 --- a/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java +++ b/src/main/java/org/jabref/model/entry/BibEntryTypeBuilder.java @@ -75,8 +75,9 @@ public BibEntryTypeBuilder withRequiredFields(List first, Field... req public BibEntryType build() { // Treat required fields as important ones Stream requiredAsImportant = requiredFields.stream() - .flatMap(Set::stream) - .map(field -> new BibField(field, FieldPriority.IMPORTANT)); + .map(OrFields::getFields) + .flatMap(Set::stream) + .map(field -> new BibField(field, FieldPriority.IMPORTANT)); Set allFields = Stream.concat(fields.stream(), requiredAsImportant).collect(Collectors.toCollection(LinkedHashSet::new)); return new BibEntryType(type, allFields, requiredFields); } diff --git a/src/main/java/org/jabref/model/entry/field/FieldFactory.java b/src/main/java/org/jabref/model/entry/field/FieldFactory.java index cac6bb3b2c77..645a1295221c 100644 --- a/src/main/java/org/jabref/model/entry/field/FieldFactory.java +++ b/src/main/java/org/jabref/model/entry/field/FieldFactory.java @@ -31,7 +31,7 @@ public static String serializeOrFields(Field... fields) { } public static String serializeOrFields(OrFields fields) { - return fields.stream() + return fields.getFields().stream() .map(field -> { if (field instanceof UnknownField unknownField) { // In case a user has put a user-defined field, the casing of that field is kept diff --git a/src/main/java/org/jabref/model/entry/field/OrFields.java b/src/main/java/org/jabref/model/entry/field/OrFields.java index baf848d43920..e0d24537be52 100644 --- a/src/main/java/org/jabref/model/entry/field/OrFields.java +++ b/src/main/java/org/jabref/model/entry/field/OrFields.java @@ -3,36 +3,80 @@ import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; import java.util.StringJoiner; -public class OrFields extends LinkedHashSet implements Comparable { +public class OrFields implements Comparable { + + private LinkedHashSet fields = new LinkedHashSet<>(); public OrFields(Field field) { - add(field); + fields.add(field); } - public OrFields(Field... fields) { - addAll(Arrays.asList(fields)); + public OrFields(Field... fieldsToAdd) { + Arrays.stream(fieldsToAdd).forEach(fields::add); } - public OrFields(Collection fields) { - addAll(fields); + public OrFields(Collection fieldsToAdd) { + fields.addAll(fieldsToAdd); } public String getDisplayName() { StringJoiner joiner = new StringJoiner("/"); - for (Field field : this) { + for (Field field : fields) { joiner.add(field.getDisplayName()); } return joiner.toString(); } public Field getPrimary() { - return this.iterator().next(); + return fields.iterator().next(); + } + + public Set getFields() { + return this.fields; + } + + public boolean contains(Field field) { + return fields.contains(field); } @Override public int compareTo(OrFields o) { return FieldFactory.serializeOrFields(this).compareTo(FieldFactory.serializeOrFields(o)); } + + public boolean hasExactlyOne() { + return this.fields.size() == 1; + } + + public boolean isEmpty() { + return this.fields.isEmpty(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrFields orFields = (OrFields) o; + return Objects.equals(fields, orFields.fields); + } + + @Override + public int hashCode() { + return Objects.hashCode(fields); + } + + @Override + public String toString() { + return "OrFields{" + + "fields=" + fields + + '}'; + } } diff --git a/src/main/java/org/jabref/model/metadata/ContentSelectors.java b/src/main/java/org/jabref/model/metadata/ContentSelectors.java index d8c70e565ee9..93c85250ae91 100644 --- a/src/main/java/org/jabref/model/metadata/ContentSelectors.java +++ b/src/main/java/org/jabref/model/metadata/ContentSelectors.java @@ -86,4 +86,12 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(contentSelectors); } + + @Override + public String toString() { + return "ContentSelectors{" + + "contentSelectors=" + contentSelectors + + ", fieldsWithSelectors=" + getFieldsWithSelectors() + + '}'; + } } diff --git a/src/main/java/org/jabref/model/metadata/SaveOrder.java b/src/main/java/org/jabref/model/metadata/SaveOrder.java index 2161d7526c5a..8186a2514c30 100644 --- a/src/main/java/org/jabref/model/metadata/SaveOrder.java +++ b/src/main/java/org/jabref/model/metadata/SaveOrder.java @@ -11,9 +11,15 @@ import org.slf4j.LoggerFactory; /** - * Stores the save order config from MetaData + * Stores the save order config for a library *

- * Format: <choice>, a pair of field + ascending (boolean) + * Format: <choice> ({@link OrderType}, a pair of {@link Field} + descending (boolean) + *

+ *

+ * Note that {@link OrderType#TABLE} can only be used as "intermediate" setting. When passing SaveOrder + * to {@link org.jabref.logic.exporter.BibDatabaseWriter}, the orderType must be different. Reason: The writer + * does not have access to the UI. + *

*/ public class SaveOrder { @@ -61,8 +67,8 @@ public static SaveOrder parse(List orderedData) { return new SaveOrder(orderedData); } - public static SaveOrder getDefaultSaveOrder() { - return new SaveOrder(OrderType.ORIGINAL, List.of()); + public static SelfContainedSaveOrder getDefaultSaveOrder() { + return new SelfContainedSaveOrder(OrderType.ORIGINAL, List.of()); } public OrderType getOrderType() { @@ -102,11 +108,7 @@ public String toString() { */ public List getAsStringList() { List res = new ArrayList<>(7); - if (orderType == OrderType.ORIGINAL) { - res.add(OrderType.ORIGINAL.toString()); - } else { - res.add(OrderType.SPECIFIED.toString()); - } + res.add(orderType.toString()); for (SortCriterion sortCriterion : sortCriteria) { res.add(sortCriterion.field.getName()); diff --git a/src/main/java/org/jabref/model/metadata/SelfContainedSaveOrder.java b/src/main/java/org/jabref/model/metadata/SelfContainedSaveOrder.java new file mode 100644 index 000000000000..1c40945e9edc --- /dev/null +++ b/src/main/java/org/jabref/model/metadata/SelfContainedSaveOrder.java @@ -0,0 +1,27 @@ +package org.jabref.model.metadata; + +import java.util.List; + +/** + * With this class, the user of an instance can directly sort things. Without looking up anything in the preferences or in the UI. + */ +public class SelfContainedSaveOrder extends SaveOrder { + public SelfContainedSaveOrder(OrderType orderType, List sortCriteria) { + super(orderType, sortCriteria); + if (orderType == OrderType.TABLE) { + throw new IllegalArgumentException("TABLE requires external lookup."); + } + } + + /** + * Converts a SaveOrder to a SelfContainedSaveOrder + * + * @throws IllegalArgumentException if {@code saveOrder} has {@link OrderType#TABLE} + */ + public static SelfContainedSaveOrder of(SaveOrder saveOrder) { + if (saveOrder instanceof SelfContainedSaveOrder order) { + return order; + } + return new SelfContainedSaveOrder(saveOrder.getOrderType(), saveOrder.getSortCriteria()); + } +} diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java index e48e39553c1a..651ecc4ca858 100644 --- a/src/main/java/org/jabref/preferences/JabRefPreferences.java +++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java @@ -111,6 +111,7 @@ import org.jabref.model.entry.types.EntryTypeFactory; import org.jabref.model.groups.GroupHierarchyType; import org.jabref.model.metadata.SaveOrder; +import org.jabref.model.metadata.SelfContainedSaveOrder; import org.jabref.model.search.rules.SearchRules; import org.jabref.model.strings.StringUtil; @@ -2255,18 +2256,11 @@ private void storeExportSaveOrder(SaveOrder saveOrder) { putBoolean(EXPORT_TERTIARY_SORT_DESCENDING, saveOrder.getSortCriteria().get(2).descending); } - private SaveOrder getTableSaveOrder() { + public SaveOrder getTableSaveOrder() { List sortOrder = mainTableColumnPreferences.getColumnSortOrder(); - List criteria = new ArrayList<>(); - - for (var column : sortOrder) { - boolean descending = column.getSortType() == SortType.DESCENDING; - criteria.add(new SaveOrder.SortCriterion( - FieldFactory.parseField(column.getQualifier()), - descending)); - } - - return new SaveOrder(SaveOrder.OrderType.TABLE, criteria); + return new SaveOrder( + SaveOrder.OrderType.TABLE, + sortOrder.stream().flatMap(model -> model.getSortCriteria().stream()).toList()); } @Override @@ -2278,7 +2272,7 @@ public SaveConfiguration getExportConfiguration() { }; return new SaveConfiguration() - .withSaveOrder(saveOrder) + .withSaveOrder(SelfContainedSaveOrder.of(saveOrder)) .withReformatOnSave(getLibraryPreferences().shouldAlwaysReformatOnSave()); } diff --git a/src/main/java/org/jabref/preferences/PreferencesService.java b/src/main/java/org/jabref/preferences/PreferencesService.java index 0150a4314d27..485e93ded568 100644 --- a/src/main/java/org/jabref/preferences/PreferencesService.java +++ b/src/main/java/org/jabref/preferences/PreferencesService.java @@ -70,6 +70,9 @@ public interface PreferencesService { ImportFormatPreferences getImportFormatPreferences(); + /** + * Returns the export configuration. The contained SaveConfiguration is a {@link org.jabref.model.metadata.SelfContainedSaveOrder} + */ SaveConfiguration getExportConfiguration(); BibEntryTypesManager getCustomEntryTypesRepository(); diff --git a/src/main/resources/tinylog.properties b/src/main/resources/tinylog.properties index b4340fa32e9a..9eecb7934bd9 100644 --- a/src/main/resources/tinylog.properties +++ b/src/main/resources/tinylog.properties @@ -7,3 +7,4 @@ writerAzure = application insights exception = strip: jdk.internal #level@org.jabref.model.entry.BibEntry = debug +level@org.jabref.gui.maintable.PersistenceVisualStateTable = debug diff --git a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerDiscardedTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerDiscardedTest.java similarity index 95% rename from src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerDiscardedTest.java rename to src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerDiscardedTest.java index ee60abcc067c..e7d868ee8622 100644 --- a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerDiscardedTest.java +++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerDiscardedTest.java @@ -1,4 +1,4 @@ -package org.jabref.logic.autosaveandbackup; +package org.jabref.gui.autosaveandbackup; import java.io.IOException; import java.io.Writer; @@ -6,6 +6,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import org.jabref.gui.LibraryTab; import org.jabref.logic.exporter.AtomicFileWriter; import org.jabref.logic.exporter.BibWriter; import org.jabref.logic.exporter.BibtexDatabaseWriter; @@ -63,7 +64,7 @@ public void setup(@TempDir Path tempDir) throws Exception { saveDatabase(); - backupManager = new BackupManager(bibDatabaseContext, bibEntryTypesManager, preferencesService); + backupManager = new BackupManager(mock(LibraryTab.class), bibDatabaseContext, bibEntryTypesManager, preferencesService); makeBackup(); } diff --git a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java similarity index 93% rename from src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerTest.java rename to src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java index 3573bd7aa8bf..c1a0caf267d0 100644 --- a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerTest.java +++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java @@ -1,4 +1,4 @@ -package org.jabref.logic.autosaveandbackup; +package org.jabref.gui.autosaveandbackup; import java.nio.file.Files; import java.nio.file.Path; @@ -8,6 +8,7 @@ import java.util.List; import java.util.Optional; +import org.jabref.gui.LibraryTab; import org.jabref.logic.util.BackupFileType; import org.jabref.logic.util.OS; import org.jabref.logic.util.io.BackupFileUtil; @@ -141,7 +142,11 @@ public void shouldNotCreateABackup(@TempDir Path customDir) throws Exception { when(preferences.getFilePreferences()).thenReturn(filePreferences); when(filePreferences.getBackupDirectory()).thenReturn(backupDir); - BackupManager manager = BackupManager.start(database, mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS), preferences); + BackupManager manager = BackupManager.start( + mock(LibraryTab.class), + database, + mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS), + preferences); manager.listen(new MetaDataChangedEvent(new MetaData())); BackupManager.shutdown(database, backupDir, false); @@ -164,7 +169,11 @@ public void shouldCreateABackup(@TempDir Path customDir) throws Exception { when(filePreferences.getBackupDirectory()).thenReturn(backupDir); when(filePreferences.shouldCreateBackup()).thenReturn(true); - BackupManager manager = BackupManager.start(database, mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS), preferences); + BackupManager manager = BackupManager.start( + mock(LibraryTab.class), + database, + mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS), + preferences); manager.listen(new MetaDataChangedEvent(new MetaData())); Optional fullBackupPath = manager.determineBackupPathForNewBackup(backupDir); diff --git a/src/test/java/org/jabref/gui/exporter/ExportToClipboardActionTest.java b/src/test/java/org/jabref/gui/exporter/ExportToClipboardActionTest.java index 5321fe1ae7ca..9602c84fd59f 100644 --- a/src/test/java/org/jabref/gui/exporter/ExportToClipboardActionTest.java +++ b/src/test/java/org/jabref/gui/exporter/ExportToClipboardActionTest.java @@ -23,6 +23,7 @@ import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.types.StandardEntryType; import org.jabref.model.metadata.MetaData; +import org.jabref.model.metadata.SaveOrder; import org.jabref.preferences.FilePreferences; import org.jabref.preferences.LibraryPreferences; import org.jabref.preferences.PreferencesService; @@ -92,6 +93,7 @@ public void export(BibDatabaseContext databaseContext, Path file, List when(preferences.getFilePreferences()).thenReturn(filePreferences); when(preferences.getLibraryPreferences()).thenReturn(libraryPreferences); when(preferences.getExportPreferences().getLastExportExtension()).thenReturn("HTML"); + when(preferences.getExportConfiguration().getSaveOrder()).thenReturn(SaveOrder.getDefaultSaveOrder()); when(stateManager.getSelectedEntries()).thenReturn(selectedEntries); when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext)); // noinspection ConstantConditions since databaseContext is mocked diff --git a/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java b/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java index 59c9d1161329..47ed17128ed0 100644 --- a/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java +++ b/src/test/java/org/jabref/logic/bibtex/comparator/FieldComparatorTest.java @@ -202,25 +202,52 @@ public void compareParsableWithNonParsableNumericFieldDescending() throws Except } @ParameterizedTest - @MethodSource("provideArgumentsForNumericalComparison") - public void compareNumericalValues(int comparisonResult, String id1, String id2, String errorMessage) { + @MethodSource + public void compareNumericalValues(int comparisonResult, String id1, String id2, String message) { FieldComparator comparator = new FieldComparator(StandardField.PMID); BibEntry entry1 = new BibEntry() .withField(StandardField.PMID, id1); BibEntry entry2 = new BibEntry() .withField(StandardField.PMID, id2); - assertEquals(comparisonResult, comparator.compare(entry1, entry2), errorMessage); + assertEquals(comparisonResult, comparator.compare(entry1, entry2), message); } - private static Stream provideArgumentsForNumericalComparison() { + private static Stream compareNumericalValues() { return Stream.of( - Arguments.of(0, "123456", "123456", "IDs are lexicographically not equal [1]"), - Arguments.of(1, "234567", "123456", "234567 is lexicographically smaller than 123456"), - Arguments.of(1, "abc##z", "123456", "abc##z is lexicographically smaller than 123456 "), - Arguments.of(0, "", "", "IDs are lexicographically not equal [2]"), - Arguments.of(1, "", "123456", "No ID is lexicographically smaller than 123456"), - Arguments.of(-1, "123456", "", "123456 is lexicographically greater than no ID") + Arguments.of(0, "123456", "123456", "IDs should be lexicographically equal"), + Arguments.of(1, "234567", "123456", "234567 should be lexicographically greater than 123456"), + Arguments.of(1, "abc##z", "123456", "abc##z should be lexicographically greater than 123456 "), + Arguments.of(0, "", "", "Empty IDs should be lexicographically equal"), + Arguments.of(-1, "", "123456", "No ID should be lexicographically smaller than 123456"), + Arguments.of(1, "123456", "", "123456 should be lexicographically greater than no ID") + ); + } + + @ParameterizedTest + @MethodSource + public void nullTests(int comparisonResult, String firstValue, String secondValue) { + FieldComparator comparator = new FieldComparator(StandardField.TITLE); + + BibEntry entry1 = new BibEntry(); + if (firstValue != null) { + entry1.setField(StandardField.TITLE, firstValue); + } + + BibEntry entry2 = new BibEntry(); + if (secondValue != null) { + entry2.setField(StandardField.TITLE, secondValue); + } + + assertEquals(comparisonResult, comparator.compare(entry1, entry2)); + } + + private static Stream nullTests() { + return Stream.of( + Arguments.of(0, null, null), + Arguments.of(0, "value", "value"), + Arguments.of(-1, null, "value"), + Arguments.of(1, "value", null) ); } } diff --git a/src/test/resources/org/jabref/logic/autosaveandbackup/changes.bib b/src/test/resources/org/jabref/gui/autosaveandbackup/changes.bib similarity index 100% rename from src/test/resources/org/jabref/logic/autosaveandbackup/changes.bib rename to src/test/resources/org/jabref/gui/autosaveandbackup/changes.bib diff --git a/src/test/resources/org/jabref/logic/autosaveandbackup/changes.bib.bak b/src/test/resources/org/jabref/gui/autosaveandbackup/changes.bib.bak similarity index 100% rename from src/test/resources/org/jabref/logic/autosaveandbackup/changes.bib.bak rename to src/test/resources/org/jabref/gui/autosaveandbackup/changes.bib.bak diff --git a/src/test/resources/org/jabref/logic/autosaveandbackup/no-autosave.bib b/src/test/resources/org/jabref/gui/autosaveandbackup/no-autosave.bib similarity index 100% rename from src/test/resources/org/jabref/logic/autosaveandbackup/no-autosave.bib rename to src/test/resources/org/jabref/gui/autosaveandbackup/no-autosave.bib diff --git a/src/test/resources/org/jabref/logic/autosaveandbackup/no-changes.bib b/src/test/resources/org/jabref/gui/autosaveandbackup/no-changes.bib similarity index 100% rename from src/test/resources/org/jabref/logic/autosaveandbackup/no-changes.bib rename to src/test/resources/org/jabref/gui/autosaveandbackup/no-changes.bib diff --git a/src/test/resources/org/jabref/logic/autosaveandbackup/no-changes.bib.bak b/src/test/resources/org/jabref/gui/autosaveandbackup/no-changes.bib.bak similarity index 100% rename from src/test/resources/org/jabref/logic/autosaveandbackup/no-changes.bib.bak rename to src/test/resources/org/jabref/gui/autosaveandbackup/no-changes.bib.bak From 6030e7d3db9ef80c25516147584246bad6b0f905 Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 2 Sep 2023 21:54:19 +0200 Subject: [PATCH 3/4] Update bug_report.yml Add 5.10 --- .github/ISSUE_TEMPLATE/bug_report.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 783e39dd0577..5f136688ca4e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,8 +12,7 @@ body: attributes: label: JabRef version options: - - "5.9 (latest release)" - - "3.8.2" + - "5.10 (latest release)" - Latest development branch build (please note build date below) - Other (please describe below) description: The version as shown in the about dialog. @@ -42,7 +41,7 @@ body: - type: checkboxes attributes: - label: Checked with the latest development build + label: Checked with the latest development build (copy version output from About dialog) description: | Please always test if the bug is still reproducible in the latest development version. We are constantly improving JabRef and some bugs may already be fixed. If you already use a development version, ensure that you use the latest one. You can download the latest development build at: https://builds.jabref.org/main/ . **Please make a backup of your library before you try out this version.** From 57f97f23ac939476864791a2db58939d1a2fb819 Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 2 Sep 2023 23:40:36 +0200 Subject: [PATCH 4/4] Add method for detecting journal abbrev delimiter (#10281) --- .../logic/journals/AbbreviationFormat.java | 10 +++- .../logic/journals/AbbreviationParser.java | 57 ++++++++----------- .../logic/journals/AbbreviationWriter.java | 2 +- .../journals/JournalAbbreviationLoader.java | 2 +- .../JournalAbbreviationRepository.java | 36 ++++++------ .../JournalAbbreviationsViewModelTabTest.java | 4 +- .../journals/AbbreviationParserTest.java | 47 +++++++++++++++ .../journals/AbbreviationWriterTest.java | 4 +- 8 files changed, 103 insertions(+), 59 deletions(-) create mode 100644 src/test/java/org/jabref/logic/journals/AbbreviationParserTest.java diff --git a/src/main/java/org/jabref/logic/journals/AbbreviationFormat.java b/src/main/java/org/jabref/logic/journals/AbbreviationFormat.java index 0388b048e94b..21734cad692f 100644 --- a/src/main/java/org/jabref/logic/journals/AbbreviationFormat.java +++ b/src/main/java/org/jabref/logic/journals/AbbreviationFormat.java @@ -4,20 +4,24 @@ public final class AbbreviationFormat { - public static final char DELIMITER = ';'; + public static final char DELIMITER = ','; public static final char ESCAPE = '\\'; public static final char QUOTE = '"'; private AbbreviationFormat() { } - public static CSVFormat getCSVFormat() { + public static CSVFormat getCSVFormatWithDelimiter(char delimiter) { return CSVFormat.DEFAULT.builder() .setIgnoreEmptyLines(true) - .setDelimiter(DELIMITER) + .setDelimiter(delimiter) .setEscape(ESCAPE) .setQuote(QUOTE) .setTrim(true) .build(); } + + public static CSVFormat getCSVFormatWithDefaultDilimeter() { + return getCSVFormatWithDelimiter(DELIMITER); + } } diff --git a/src/main/java/org/jabref/logic/journals/AbbreviationParser.java b/src/main/java/org/jabref/logic/journals/AbbreviationParser.java index 9a9f67f0bfe6..5c84eb8ec17b 100644 --- a/src/main/java/org/jabref/logic/journals/AbbreviationParser.java +++ b/src/main/java/org/jabref/logic/journals/AbbreviationParser.java @@ -2,58 +2,37 @@ import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Reads abbreviation files (CSV format) into a list of Abbreviations. */ public class AbbreviationParser { - - private static final Logger LOGGER = LoggerFactory.getLogger(AbbreviationParser.class); + private static final Character[] DELIMITERS = {';', ','}; + private static final char NO_DELIMITER = '\0'; // empty char // Ensures ordering while preventing duplicates private final LinkedHashSet abbreviations = new LinkedHashSet<>(); - void readJournalListFromResource(String resourceFileName) { - try (InputStream stream = JournalAbbreviationRepository.class.getResourceAsStream(resourceFileName); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { - readJournalList(reader); - } catch (IOException e) { - LOGGER.error(String.format("Could not read journal list from file %s", resourceFileName), e); - } - } - - public void readJournalListFromFile(Path file) throws IOException { - readJournalListFromFile(file, StandardCharsets.UTF_8); - } - - public void readJournalListFromFile(Path file, Charset encoding) throws IOException { - try (BufferedReader reader = Files.newBufferedReader(file, encoding)) { - readJournalList(reader); - } - } - - /** + /* * Read the given file, which should contain a list of journal names and their abbreviations. Each line should be - * formatted as: "Full Journal Name;Abbr. Journal Name;[Shortest Unique Abbreviation]" + * formatted as: "Full Journal Name,Abbr. Journal Name,[Shortest Unique Abbreviation]" + * Tries to detect the delimiter, if comma or semicolon is used to ensure backwards compatibility * - * @param reader a given file into a Reader object + * @param file Path the given file */ - private void readJournalList(Reader reader) throws IOException { - try (CSVParser csvParser = new CSVParser(reader, AbbreviationFormat.getCSVFormat())) { + void readJournalListFromFile(Path file) throws IOException { + char delimiter = detectDelimiter(file); + + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(file, StandardCharsets.UTF_8), AbbreviationFormat.getCSVFormatWithDelimiter(delimiter))) { for (CSVRecord csvRecord : csvParser) { String name = csvRecord.size() > 0 ? csvRecord.get(0) : ""; String abbreviation = csvRecord.size() > 1 ? csvRecord.get(1) : ""; @@ -70,6 +49,20 @@ private void readJournalList(Reader reader) throws IOException { } } + private char detectDelimiter(Path file) throws IOException { + try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { + String line = reader.readLine(); + + if (line == null) { + return NO_DELIMITER; + } + return Arrays.stream(DELIMITERS) + .filter(s -> line.contains(s.toString())) + .findFirst() + .orElse(NO_DELIMITER); + } + } + public Collection getAbbreviations() { return abbreviations; } diff --git a/src/main/java/org/jabref/logic/journals/AbbreviationWriter.java b/src/main/java/org/jabref/logic/journals/AbbreviationWriter.java index 4a3054f7634a..e60954c521b5 100644 --- a/src/main/java/org/jabref/logic/journals/AbbreviationWriter.java +++ b/src/main/java/org/jabref/logic/journals/AbbreviationWriter.java @@ -26,7 +26,7 @@ private AbbreviationWriter() { */ public static void writeOrCreate(Path path, List abbreviations) throws IOException { try (OutputStreamWriter writer = new OutputStreamWriter(Files.newOutputStream(path), StandardCharsets.UTF_8); - CSVPrinter csvPrinter = new CSVPrinter(writer, AbbreviationFormat.getCSVFormat())) { + CSVPrinter csvPrinter = new CSVPrinter(writer, AbbreviationFormat.getCSVFormatWithDefaultDilimeter())) { for (Abbreviation entry : abbreviations) { if (entry.isDefaultShortestUniqueAbbreviation()) { csvPrinter.printRecord(entry.getName(), entry.getAbbreviation()); diff --git a/src/main/java/org/jabref/logic/journals/JournalAbbreviationLoader.java b/src/main/java/org/jabref/logic/journals/JournalAbbreviationLoader.java index 1c0717a943c7..2607293ac108 100644 --- a/src/main/java/org/jabref/logic/journals/JournalAbbreviationLoader.java +++ b/src/main/java/org/jabref/logic/journals/JournalAbbreviationLoader.java @@ -46,7 +46,7 @@ public static JournalAbbreviationRepository loadRepository(JournalAbbreviationPr // Read external lists List lists = journalAbbreviationPreferences.getExternalJournalLists(); - if (!(lists.isEmpty())) { + if (lists != null && !(lists.isEmpty())) { // reversing ensures that the latest lists overwrites the former one Collections.reverse(lists); for (String filename : lists) { diff --git a/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java b/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java index ca0db044368b..10f9778f825b 100644 --- a/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java +++ b/src/main/java/org/jabref/logic/journals/JournalAbbreviationRepository.java @@ -27,22 +27,23 @@ public class JournalAbbreviationRepository { private final TreeSet customAbbreviations = new TreeSet<>(); public JournalAbbreviationRepository(Path journalList) { - MVStore store = new MVStore.Builder().readOnly().fileName(journalList.toAbsolutePath().toString()).open(); - MVMap mvFullToAbbreviationObject = store.openMap("FullToAbbreviation"); - - mvFullToAbbreviationObject.forEach((name, abbreviation) -> { - String abbrevationString = abbreviation.getAbbreviation(); - String shortestUniqueAbbreviation = abbreviation.getShortestUniqueAbbreviation(); - Abbreviation newAbbreviation = new Abbreviation( - name, - abbrevationString, - shortestUniqueAbbreviation - ); - fullToAbbreviationObject.put(name, newAbbreviation); - abbreviationToAbbreviationObject.put(abbrevationString, newAbbreviation); - dotlessToAbbreviationObject.put(newAbbreviation.getDotlessAbbreviation(), newAbbreviation); - shortestUniqueToAbbreviationObject.put(shortestUniqueAbbreviation, newAbbreviation); - }); + MVMap mvFullToAbbreviationObject; + try (MVStore store = new MVStore.Builder().readOnly().fileName(journalList.toAbsolutePath().toString()).open()) { + mvFullToAbbreviationObject = store.openMap("FullToAbbreviation"); + mvFullToAbbreviationObject.forEach((name, abbreviation) -> { + String abbrevationString = abbreviation.getAbbreviation(); + String shortestUniqueAbbreviation = abbreviation.getShortestUniqueAbbreviation(); + Abbreviation newAbbreviation = new Abbreviation( + name, + abbrevationString, + shortestUniqueAbbreviation + ); + fullToAbbreviationObject.put(name, newAbbreviation); + abbreviationToAbbreviationObject.put(abbrevationString, newAbbreviation); + dotlessToAbbreviationObject.put(newAbbreviation.getDotlessAbbreviation(), newAbbreviation); + shortestUniqueToAbbreviationObject.put(shortestUniqueAbbreviation, newAbbreviation); + }); + } } private static boolean isMatched(String name, Abbreviation abbreviation) { @@ -57,10 +58,9 @@ private static boolean isMatchedAbbreviated(String name, Abbreviation abbreviati if (isExpanded) { return false; } - boolean isAbbreviated = name.equalsIgnoreCase(abbreviation.getAbbreviation()) + return name.equalsIgnoreCase(abbreviation.getAbbreviation()) || name.equalsIgnoreCase(abbreviation.getDotlessAbbreviation()) || name.equalsIgnoreCase(abbreviation.getShortestUniqueAbbreviation()); - return isAbbreviated; } /** diff --git a/src/test/java/org/jabref/gui/preferences/journals/JournalAbbreviationsViewModelTabTest.java b/src/test/java/org/jabref/gui/preferences/journals/JournalAbbreviationsViewModelTabTest.java index 62cf94d2b7ae..433d736862dd 100644 --- a/src/test/java/org/jabref/gui/preferences/journals/JournalAbbreviationsViewModelTabTest.java +++ b/src/test/java/org/jabref/gui/preferences/journals/JournalAbbreviationsViewModelTabTest.java @@ -85,9 +85,9 @@ public TestAbbreviation(String name, String abbreviation, String shortestUniqueA @Override public String toString() { if (showShortestUniqueAbbreviation) { - return this.getName() + ";" + this.getAbbreviation() + ";" + this.getShortestUniqueAbbreviation(); + return this.getName() + "," + this.getAbbreviation() + "," + this.getShortestUniqueAbbreviation(); } - return this.getName() + ";" + this.getAbbreviation(); + return this.getName() + "," + this.getAbbreviation(); } } diff --git a/src/test/java/org/jabref/logic/journals/AbbreviationParserTest.java b/src/test/java/org/jabref/logic/journals/AbbreviationParserTest.java new file mode 100644 index 000000000000..3237b60588ad --- /dev/null +++ b/src/test/java/org/jabref/logic/journals/AbbreviationParserTest.java @@ -0,0 +1,47 @@ +package org.jabref.logic.journals; + +import java.io.BufferedWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AbbreviationParserTest { + + private Path csvFile; + private final AbbreviationParser parser = new AbbreviationParser(); + + private final Abbreviation abbreviation = new Abbreviation("Long Name", "L.N.", "L.N."); + + @BeforeEach + void setup(@TempDir Path tempDir) { + csvFile = tempDir.resolve("test.csv"); + } + + @Test + void testReadingFileFromCSVWithSemicolon() throws Exception { + // String name, String abbreviation, String shortestUniqueAbbreviation + String testAbbrev = "Long Name;L.N.;L.N."; + try (BufferedWriter writer = Files.newBufferedWriter(csvFile, StandardCharsets.UTF_8)) { + writer.write(testAbbrev); + } + parser.readJournalListFromFile(csvFile); + assertEquals(Set.of(abbreviation), parser.getAbbreviations()); + } + + @Test + void testReadingFileFromCSVWithComma() throws Exception { + String testAbbrev = "Long Name,L.N.,L.N."; + try (BufferedWriter writer = Files.newBufferedWriter(csvFile, StandardCharsets.UTF_8)) { + writer.write(testAbbrev); + } + parser.readJournalListFromFile(csvFile); + assertEquals(Set.of(abbreviation), parser.getAbbreviations()); + } +} diff --git a/src/test/java/org/jabref/logic/journals/AbbreviationWriterTest.java b/src/test/java/org/jabref/logic/journals/AbbreviationWriterTest.java index c16e24b23abe..bf207b25da75 100644 --- a/src/test/java/org/jabref/logic/journals/AbbreviationWriterTest.java +++ b/src/test/java/org/jabref/logic/journals/AbbreviationWriterTest.java @@ -21,7 +21,7 @@ void shortestUniqueAbbreviationWrittenIfItDiffers(@TempDir Path tempDir) throws AbbreviationWriter.writeOrCreate( csvFile, List.of(abbreviation)); - assertEquals(List.of("Full;Abbr;A"), Files.readAllLines(csvFile)); + assertEquals(List.of("Full,Abbr,A"), Files.readAllLines(csvFile)); } @Test @@ -31,6 +31,6 @@ void doNotWriteShortestUniqueAbbreviationWrittenIfItDiffers(@TempDir Path tempDi AbbreviationWriter.writeOrCreate( csvFile, List.of(abbreviation)); - assertEquals(List.of("Full;Abbr"), Files.readAllLines(csvFile)); + assertEquals(List.of("Full,Abbr"), Files.readAllLines(csvFile)); } }