From d792923b51e2a11b8445424d6a5ace41fdc1eaa9 Mon Sep 17 00:00:00 2001 From: Christoph Date: Mon, 18 Mar 2024 12:00:42 +0100 Subject: [PATCH] Change copy-paste function to handle string constants (follow up PR) (#11037) * [Copy] Include string constants in copy (#11) Signed-off-by: Anders Blomqvist * [Copy] New method for serializing string constants (#12) Signed-off-by: Anders Blomqvist * Add a sanity check for null for clipboard content Currenlty, the clipboard content can be null since the database does not seem to be updating. This is a sanity check to prevent the program from adding null to the clipboard. Link to DD2480-Group1/jabref#13 * [Fix] Add parsed serilization when save settings When loading from existing files or libraries, the parser will set the serilization of the string constant to the correct value. However, when editing via the GUI, the serilization was not set and a new string constant list will be created without the serilization. This result in the serilization being null and when copying with the clipboard. Link to DD2480-Group1/jabref#13 * feat: import string constants when pasting #9 Add functionality to import string constants in the paste function Should add functionality to handle colliding string constants. Should also check that the constants are valid using the ConstantsItemModel class. * feat: Add string constant validity checker and dialog messages #9 Check that a pasted string constant is valid using the ConstantsItemModel class. Add diagnostic messages notifying users when adding a string constant fails while pasting. * [Copy] Copy referenced constant strings to clipboard (#16) * feat: Add parsed serialized string when cloning * feat: Add sanity check for null in ClipBoardManager * closes #15 * feat: new unit tests Add 4 new unit tests, testing the new features added for issue-10872. Specifically the tests are for the `storeSettings` method in the ConstantsPropertiesViewModel.java, and `setContent` in the ClipBaordManager.java. Closes #6 * Update CHANGELOG with copy and paste function * Fix Checkstyle failing by reformat the code * Fix OpenRewrite failing by running rewriteRun * Refactor by extract methods in setContent * collet failures * changelog and use os.newline * checkstyle * use real bibentrytypes manager * Fix CHANGELOG.md * Swap if branches * Code cleanup * Use List for getUsedStringValues * Fix submodule * Collection is better * Fix csl-styles * Remove empty line * Group BibTeX string l10n together --------- Signed-off-by: Anders Blomqvist Co-authored-by: Anders Blomqvist Co-authored-by: ZOU Hetai <33616271+JXNCTED@users.noreply.github.com> Co-authored-by: Hannes Stig Co-authored-by: Elliot Co-authored-by: Oliver Kopp --- CHANGELOG.md | 1 + .../java/org/jabref/gui/ClipBoardManager.java | 22 ++- .../jabref/gui/edit/ReplaceStringAction.java | 1 - .../gui/externalfiles/ImportHandler.java | 30 +++- .../ConstantsPropertiesViewModel.java | 12 +- .../org/jabref/gui/maintable/MainTable.java | 12 +- .../importer/fileformat/BibtexParser.java | 4 + .../jabref/model/database/BibDatabase.java | 21 ++- .../org/jabref/model/entry/BibtexString.java | 3 + src/main/resources/l10n/JabRef_en.properties | 57 ++++---- .../org/jabref/gui/ClipBoardManagerTest.java | 128 ++++++++++++++++++ .../ConstantsPropertiesViewModelTest.java | 62 +++++++++ .../model/database/BibDatabaseTest.java | 12 +- 13 files changed, 299 insertions(+), 66 deletions(-) create mode 100644 src/test/java/org/jabref/gui/ClipBoardManagerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f1ce0c6be..a6af81aef3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added the ability to zoom in and out in the document viewer using Ctrl + Scroll. [#10964](https://github.com/JabRef/jabref/pull/10964) - We added a Cleanup for removing non-existent files and grouped the related options [#10929](https://github.com/JabRef/jabref/issues/10929) - We added the functionality to parse the bibliography of PDFs using the GROBID online service. [#10200](https://github.com/JabRef/jabref/issues/10200) +- We added support for BibTeX String constants during copy & paste between libraries [#10872](https://github.com/JabRef/jabref/issues/10872) ### Changed diff --git a/src/main/java/org/jabref/gui/ClipBoardManager.java b/src/main/java/org/jabref/gui/ClipBoardManager.java index 792c4733384..f90dc1b0f36 100644 --- a/src/main/java/org/jabref/gui/ClipBoardManager.java +++ b/src/main/java/org/jabref/gui/ClipBoardManager.java @@ -21,6 +21,7 @@ import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.entry.BibtexString; import org.jabref.preferences.PreferencesService; import org.slf4j.Logger; @@ -155,14 +156,23 @@ public void setContent(String string) { } public void setContent(List entries, BibEntryTypesManager entryTypesManager) throws IOException { - final ClipboardContent content = new ClipboardContent(); - BibEntryWriter writer = new BibEntryWriter(new FieldWriter(preferencesService.getFieldPreferences()), entryTypesManager); - String serializedEntries = writer.serializeAll(entries, BibDatabaseMode.BIBTEX); + String serializedEntries = serializeEntries(entries, entryTypesManager); + setContent(serializedEntries); + } + + public void setContent(List entries, BibEntryTypesManager entryTypesManager, List stringConstants) throws IOException { + StringBuilder builder = new StringBuilder(); + stringConstants.forEach(strConst -> builder.append(strConst.getParsedSerialization() == null ? "" : strConst.getParsedSerialization())); + String serializedEntries = serializeEntries(entries, entryTypesManager); + builder.append(serializedEntries); + setContent(builder.toString()); + } + + private String serializeEntries(List entries, BibEntryTypesManager entryTypesManager) throws IOException { // BibEntry is not Java serializable. Thus, we need to do the serialization manually // At reading of the clipboard in JabRef, we parse the plain string in all cases, so we don't need to flag we put BibEntries here // Furthermore, storing a string also enables other applications to work with the data - content.putString(serializedEntries); - clipboard.setContent(content); - setPrimaryClipboardContent(content); + BibEntryWriter writer = new BibEntryWriter(new FieldWriter(preferencesService.getFieldPreferences()), entryTypesManager); + return writer.serializeAll(entries, BibDatabaseMode.BIBTEX); } } diff --git a/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java b/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java index 8339b5db7f3..3a7ba00795c 100644 --- a/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java +++ b/src/main/java/org/jabref/gui/edit/ReplaceStringAction.java @@ -15,7 +15,6 @@ public class ReplaceStringAction extends SimpleCommand { public ReplaceStringAction(Supplier tabSupplier, StateManager stateManager, DialogService dialogService) { this.tabSupplier = tabSupplier; this.dialogService = dialogService; - this.executable.bind(ActionHelper.needsDatabase(stateManager)); } diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index 18ac20841ee..b7fffe46b7f 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -18,6 +18,7 @@ import org.jabref.gui.StateManager; import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; import org.jabref.gui.fieldeditors.LinkedFileViewModel; +import org.jabref.gui.libraryproperties.constants.ConstantsItemModel; import org.jabref.gui.undo.UndoableInsertEntries; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; @@ -40,7 +41,9 @@ import org.jabref.logic.util.io.FileUtil; import org.jabref.model.FieldChange; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.database.KeyCollisionException; import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibtexString; import org.jabref.model.entry.LinkedFile; import org.jabref.model.entry.field.StandardField; import org.jabref.model.entry.identifier.ArXivIdentifier; @@ -311,13 +314,36 @@ private void generateKeys(List entries) { public List handleBibTeXData(String entries) { BibtexParser parser = new BibtexParser(preferencesService.getImportFormatPreferences(), fileUpdateMonitor); try { - return parser.parseEntries(new ByteArrayInputStream(entries.getBytes(StandardCharsets.UTF_8))); + List result = parser.parseEntries(new ByteArrayInputStream(entries.getBytes(StandardCharsets.UTF_8))); + Collection stringConstants = parser.getStringValues(); + importStringConstantsWithDuplicateCheck(stringConstants); + return result; } catch (ParseException ex) { LOGGER.error("Could not paste", ex); return Collections.emptyList(); } } + public void importStringConstantsWithDuplicateCheck(Collection stringConstants) { + List failures = new ArrayList<>(); + + for (BibtexString stringConstantToAdd : stringConstants) { + try { + ConstantsItemModel checker = new ConstantsItemModel(stringConstantToAdd.getName(), stringConstantToAdd.getContent()); + if (checker.combinedValidationValidProperty().get()) { + bibDatabaseContext.getDatabase().addString(stringConstantToAdd); + } else { + failures.add(Localization.lang("String constant \"%0\" was not imported because it is not a valid string constant", stringConstantToAdd.getName())); + } + } catch (KeyCollisionException ex) { + failures.add(Localization.lang("String constant %0 was not imported because it already exists in this library", stringConstantToAdd.getName())); + } + } + if (!failures.isEmpty()) { + dialogService.showWarningDialogAndWait(Localization.lang("Importing String constants"), Localization.lang("Could not import the following string constants:\n %0", String.join("\n", failures))); + } + } + public List handleStringData(String data) throws FetcherException { if ((data == null) || data.isEmpty()) { return Collections.emptyList(); @@ -356,7 +382,7 @@ private List tryImportFormats(String data) { } private List fetchByDOI(DOI doi) throws FetcherException { - LOGGER.info("Found DOI identifer in clipboard"); + LOGGER.info("Found DOI identifier in clipboard"); Optional entry = new DoiFetcher(preferencesService.getImportFormatPreferences()).performSearchById(doi.getDOI()); return OptionalUtil.toList(entry); } diff --git a/src/main/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModel.java b/src/main/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModel.java index 36f4ddeff07..f29079199c8 100644 --- a/src/main/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModel.java +++ b/src/main/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModel.java @@ -1,9 +1,9 @@ package org.jabref.gui.libraryproperties.constants; import java.util.Comparator; +import java.util.List; import java.util.Locale; import java.util.Optional; -import java.util.stream.Collectors; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ListProperty; @@ -18,6 +18,7 @@ import org.jabref.gui.libraryproperties.PropertiesTabViewModel; import org.jabref.logic.bibtex.comparator.BibtexStringComparator; import org.jabref.logic.help.HelpFile; +import org.jabref.logic.util.OS; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibtexString; import org.jabref.preferences.FilePreferences; @@ -86,9 +87,12 @@ private ConstantsItemModel convertFromBibTexString(BibtexString bibtexString) { @Override public void storeSettings() { - databaseContext.getDatabase().setStrings(stringsListProperty.stream() - .map(this::fromBibtexStringViewModel) - .collect(Collectors.toList())); + List strings = stringsListProperty.stream() + .map(this::fromBibtexStringViewModel) + .toList(); + strings.forEach(string -> string.setParsedSerialization("@String{" + + string.getName() + " = " + string.getContent() + "}" + OS.NEWLINE)); + databaseContext.getDatabase().setStrings(strings); } private BibtexString fromBibtexStringViewModel(ConstantsItemModel viewModel) { diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index e778fa13425..eb460dd0e94 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -51,6 +51,7 @@ import org.jabref.model.database.event.EntriesAddedEvent; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.entry.BibtexString; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; @@ -257,8 +258,13 @@ public void copy() { List selectedEntries = getSelectedEntries(); if (!selectedEntries.isEmpty()) { + List stringConstants = getUsedStringValues(selectedEntries); try { - clipBoardManager.setContent(selectedEntries, entryTypesManager); + if (stringConstants.isEmpty()) { + clipBoardManager.setContent(selectedEntries, entryTypesManager); + } else { + clipBoardManager.setContent(selectedEntries, entryTypesManager, stringConstants); + } dialogService.notify(Localization.lang("Copied %0 entry(ies)", selectedEntries.size())); } catch (IOException e) { LOGGER.error("Error while copying selected entries to clipboard.", e); @@ -489,4 +495,8 @@ private Optional findEntry(BibEntry entry) { .filter(viewModel -> viewModel.getEntry().equals(entry)) .findFirst(); } + + private List getUsedStringValues(List entries) { + return database.getDatabase().getUsedStrings(entries); + } } diff --git a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java index fa535dab56b..7669712a465 100644 --- a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java +++ b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java @@ -144,6 +144,10 @@ public List parseEntries(InputStream inputStream) throws ParseExceptio } } + public Collection getStringValues() { + return database.getStringValues(); + } + public Optional parseSingleEntry(String bibtexString) throws ParseException { return parseEntries(bibtexString).stream().findFirst(); } diff --git a/src/main/java/org/jabref/model/database/BibDatabase.java b/src/main/java/org/jabref/model/database/BibDatabase.java index 12f1ba3f08b..b28f6cc6398 100644 --- a/src/main/java/org/jabref/model/database/BibDatabase.java +++ b/src/main/java/org/jabref/model/database/BibDatabase.java @@ -370,27 +370,22 @@ public String resolveForStrings(String content) { /** * Get all strings used in the entries. */ - public Collection getUsedStrings(Collection entries) { - List result = new ArrayList<>(); + public List getUsedStrings(Collection entries) { Set allUsedIds = new HashSet<>(); - // All entries - for (BibEntry entry : entries) { - for (String fieldContent : entry.getFieldValues()) { - resolveContent(fieldContent, new HashSet<>(), allUsedIds); - } - } - // Preamble if (preamble != null) { resolveContent(preamble, new HashSet<>(), allUsedIds); } - for (String stringId : allUsedIds) { - result.add((BibtexString) bibtexStrings.get(stringId).clone()); + // All entries + for (BibEntry entry : entries) { + for (String fieldContent : entry.getFieldValues()) { + resolveContent(fieldContent, new HashSet<>(), allUsedIds); + } } - return result; + return allUsedIds.stream().map(bibtexStrings::get).toList(); } /** @@ -459,7 +454,7 @@ private String resolveString(String label, Set usedIds, Set allU // circular reference, and have to stop to avoid // infinite recursion. if (usedIds.contains(string.getId())) { - LOGGER.info("Stopped due to circular reference in strings: " + label); + LOGGER.info("Stopped due to circular reference in strings: {}", label); return label; } // If not, log this string's ID now. diff --git a/src/main/java/org/jabref/model/entry/BibtexString.java b/src/main/java/org/jabref/model/entry/BibtexString.java index d8e83fb6477..3515e0584e9 100644 --- a/src/main/java/org/jabref/model/entry/BibtexString.java +++ b/src/main/java/org/jabref/model/entry/BibtexString.java @@ -158,6 +158,9 @@ public String getUserComments() { public Object clone() { BibtexString clone = new BibtexString(name, content); clone.setId(id); + if (parsedSerialization != null) { + clone.setParsedSerialization(parsedSerialization); + } return clone; } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index e6c1342058c..d72fceb0f63 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -60,6 +60,30 @@ Added\ group\ "%0".=Added group "%0". Added\ string\:\ '%0'=Added string: '%0' Added\ string=Added string +Edit\ strings=Edit strings +Duplicate\ string\ name=Duplicate string name +Modified\ string=Modified string +Modified\ string\:\ '%0' =Modified string: '%0' +New\ string=New string +Remove\ string\ %0=Remove string %0 +Deleted\ string=Deleted string +Deleted\ string\:\ '%0'=Deleted string: '%0' +Renamed\ string\:\ '%0'=Renamed string: '%0' +Please\ enter\ the\ string's\ label=Please enter the string's label +Resolve\ BibTeX\ strings=Resolve BibTeX strings +The\ label\ of\ the\ string\ cannot\ be\ a\ number.=The label of the string cannot be a number. +The\ label\ of\ the\ string\ cannot\ contain\ spaces.=The label of the string cannot contain spaces. +The\ label\ of\ the\ string\ cannot\ contain\ the\ '\#'\ character.=The label of the string cannot contain the '#' character. +String\ dialog,\ add\ string=String dialog, add string +String\ dialog,\ remove\ string=String dialog, remove string +Add\ new\ String=Add new String +String\ constants=String constants +Must\ not\ be\ empty\!=Must not be empty\! +A\ string\ with\ the\ label\ '%0'\ already\ exists.=A string with the label '%0' already exists. +String\ constant\ "%0"\ was\ not\ imported\ because\ it\ is\ not\ a\ valid\ string\ constant=String constant "%0" was not imported because it is not a valid string constant +String\ constant\ %0\ was\ not\ imported\ because\ it\ already\ exists\ in\ this\ library=String constant %0 was not imported because it already exists in this library +Could\ not\ import\ the\ following\ string\ constants\:\n\ %0=Could not import the following string constants:\n %0 +Importing\ String\ constants=Importing String constants All\ entries=All entries @@ -260,8 +284,6 @@ Duplicate\ fields=Duplicate fields Unable\ to\ change\ field\ name\.\ "%0"\ already\ in\ use.=Unable to change field name. "%0" already in use. -Duplicate\ string\ name=Duplicate string name - Duplicates\ found=Duplicates found Dynamically\ group\ entries\ by\ a\ free-form\ search\ expression=Dynamically group entries by a free-form search expression @@ -276,8 +298,6 @@ Edit\ file\ type=Edit file type Edit\ group=Edit group -Edit\ strings=Edit strings - empty\ library=empty library Autocompletion=Autocompletion @@ -526,9 +546,6 @@ Modified\ group\ "%0".=Modified group "%0". Modified\ groups=Modified groups -Modified\ string=Modified string -Modified\ string\:\ '%0' =Modified string: '%0' - Modify=Modify move\ group=move group @@ -557,8 +574,6 @@ New\ BibTeX\ sublibrary=New BibTeX sublibrary New\ group=New group -New\ string=New string - Next\ entry=Next entry no\ base-BibTeX-file\ specified!=no base-BibTeX-file specified! @@ -631,8 +646,6 @@ File\ has\ no\ attached\ annotations=File has no attached annotations Please\ enter\ a\ name\ for\ the\ group.=Please enter a name for the group. -Please\ enter\ the\ string's\ label=Please enter the string's label - Please\ restart\ JabRef\ for\ preferences\ to\ take\ effect.=Please restart JabRef for preferences to take effect. Possible\ duplicate\ entries=Possible duplicate entries @@ -714,15 +727,8 @@ Remove\ group\ "%0"?=Remove group "%0"? Remove\ link=Remove link -Remove\ string\ %0=Remove string %0 - Removed\ group\ "%0".=Removed group "%0". -Deleted\ string=Deleted string -Deleted\ string\:\ '%0'=Deleted string: '%0' - -Renamed\ string\:\ '%0'=Renamed string: '%0' - Replace=Replace Replace\ With\:=Replace With: Limit\ to\ Selected\ Entries=Limit to Selected Entries @@ -741,8 +747,6 @@ Replaces\ Unicode\ ligatures\ with\ their\ expanded\ form=Replaces Unicode ligat Required\ fields=Required fields -Resolve\ BibTeX\ strings=Resolve BibTeX strings - resolved=resolved Restart=Restart @@ -893,12 +897,6 @@ Switches\ between\ full\ and\ abbreviated\ journal\ name\ if\ the\ journal\ name the\ field\ %0=the field %0 The\ group\ "%0"\ already\ contains\ the\ selection.=The group "%0" already contains the selection. -The\ label\ of\ the\ string\ cannot\ be\ a\ number.=The label of the string cannot be a number. - -The\ label\ of\ the\ string\ cannot\ contain\ spaces.=The label of the string cannot contain spaces. - -The\ label\ of\ the\ string\ cannot\ contain\ the\ '\#'\ character.=The label of the string cannot contain the '#' character. - The\ output\ option\ depends\ on\ a\ valid\ import\ option.=The output option depends on a valid import option. This\ operation\ requires\ all\ selected\ entries\ to\ have\ citation\ keys\ defined.=This operation requires all selected entries to have citation keys defined. @@ -1385,8 +1383,6 @@ Push\ to\ application=Push to application Refresh\ OpenOffice/LibreOffice=Refresh OpenOffice/LibreOffice Resolve\ duplicate\ citation\ keys=Resolve duplicate citation keys Save\ all=Save all -String\ dialog,\ add\ string=String dialog, add string -String\ dialog,\ remove\ string=String dialog, remove string Synchronize\ files=Synchronize files Unabbreviate=Unabbreviate should\ contain\ a\ protocol=should contain a protocol @@ -1978,8 +1974,6 @@ Total\ items\ found\:=Total items found: Selected\ items\:=Selected items: Download\ linked\ online\ files=Download linked online files Select\ the\ entries\ to\ be\ imported\:=Select the entries to be imported\: -Add\ new\ String=Add new String -Must\ not\ be\ empty\!=Must not be empty\! Open\ Help\ page=Open Help page Add\ new\ keyword=Add new keyword Keyword\:=Keyword: @@ -1988,7 +1982,6 @@ Keyword\ separator=Keyword separator Remove\ keyword=Remove keyword Are\ you\ sure\ you\ want\ to\ remove\ keyword\:\ \"%0\"?=Are you sure you want to remove keyword: "%0"? Reset\ to\ default=Reset to default -String\ constants=String constants Export\ all\ entries=Export all entries Generate\ citation\ keys=Generate citation keys New\ library=New library @@ -2008,8 +2001,6 @@ Set\ rank\ to\ three=Set rank to three Set\ rank\ to\ four=Set rank to four Set\ rank\ to\ five=Set rank to five -A\ string\ with\ the\ label\ '%0'\ already\ exists.=A string with the label '%0' already exists. - Executing\ command\ "%0"...=Executing command "%0"... Rename\ file\ to\ a\ given\ name=Rename file to a given name diff --git a/src/test/java/org/jabref/gui/ClipBoardManagerTest.java b/src/test/java/org/jabref/gui/ClipBoardManagerTest.java new file mode 100644 index 00000000000..087e6573b9d --- /dev/null +++ b/src/test/java/org/jabref/gui/ClipBoardManagerTest.java @@ -0,0 +1,128 @@ +package org.jabref.gui; + +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.input.Clipboard; + +import org.jabref.architecture.AllowedToUseAwt; +import org.jabref.logic.bibtex.FieldPreferences; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.entry.BibtexString; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.preferences.PreferencesService; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@AllowedToUseAwt("Requires AWT for clipboard access") +public class ClipBoardManagerTest { + + private BibEntryTypesManager entryTypesManager; + private ClipBoardManager clipBoardManager; + + @BeforeEach + void setUp() { + // create preference service mock + PreferencesService preferencesService = mock(PreferencesService.class); + FieldPreferences fieldPreferences = mock(FieldPreferences.class); + List fields = List.of(StandardField.URL); + ObservableList nonWrappableFields = FXCollections.observableArrayList(fields); + // set up mock behaviours for preferences service + when(fieldPreferences.getNonWrappableFields()).thenReturn(nonWrappableFields); + when(preferencesService.getFieldPreferences()).thenReturn(fieldPreferences); + + // create mock clipboard + Clipboard clipboard = mock(Clipboard.class); + // create primary clipboard and set a temporary value + StringSelection selection = new StringSelection("test"); + java.awt.datatransfer.Clipboard clipboardPrimary = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboardPrimary.setContents(selection, selection); + + // create mock entry manager and set up behaviour for mock + entryTypesManager = new BibEntryTypesManager(); + + // initialize a clipBoardManager + clipBoardManager = new ClipBoardManager(clipboard, clipboardPrimary, preferencesService); + } + + @DisplayName("Check that the ClipBoardManager can set a bibentry as its content from the clipboard") + @Test + void copyStringBibEntry() throws IOException { + // Arrange + String expected = "@Article{,\n author = {Claudepierre, S. G.},\n journal = {IEEE},\n}"; + + // create BibEntry + BibEntry bibEntry = new BibEntry(); + // construct an entry + bibEntry.setType(StandardEntryType.Article); + bibEntry.setField(StandardField.JOURNAL, "IEEE"); + bibEntry.setField(StandardField.AUTHOR, "Claudepierre, S. G."); + // add entry to list + List bibEntries = new ArrayList<>(); + bibEntries.add(bibEntry); + + // Act + clipBoardManager.setContent(bibEntries, entryTypesManager); + + // Assert + String actual = ClipBoardManager.getContentsPrimary(); + // clean strings + actual = actual.replaceAll("\\s+", " ").trim(); + expected = expected.replaceAll("\\s+", " ").trim(); + + assertEquals(expected, actual); + } + + @Test + @DisplayName("Check that the ClipBoardManager can handle a bibentry with string constants correctly from the clipboard") + void copyStringBibEntryWithStringConstants() throws IOException { + // Arrange + String expected = "@String{grl = \"Geophys. Res. Lett.\"}@Article{,\n" + " author = {Claudepierre, S. G.},\n" + + " journal = {grl},\n" + "}"; + // create BibEntry + BibEntry bibEntry = new BibEntry(); + // construct an entry + bibEntry.setType(StandardEntryType.Article); + bibEntry.setField(StandardField.JOURNAL, "grl"); + bibEntry.setField(StandardField.AUTHOR, "Claudepierre, S. G."); + // add entry to list + List bibEntries = new ArrayList<>(); + bibEntries.add(bibEntry); + + // string constants + List constants = new ArrayList<>(); + + // Mock BibtexString + BibtexString bibtexString = mock(BibtexString.class); + + // define return value for getParsedSerialization() + when(bibtexString.getParsedSerialization()).thenReturn("@String{grl = \"Geophys. Res. Lett.\"}"); + // add the constant + constants.add(bibtexString); + + // Act + clipBoardManager.setContent(bibEntries, entryTypesManager, constants); + + // Assert + String actual = ClipBoardManager.getContentsPrimary(); + // clean strings + actual = actual.replaceAll("\\s+", " ").trim(); + expected = expected.replaceAll("\\s+", " ").trim(); + + assertEquals(expected, actual); + } +} diff --git a/src/test/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModelTest.java b/src/test/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModelTest.java index 60311301dee..d7a75a60be3 100644 --- a/src/test/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModelTest.java +++ b/src/test/java/org/jabref/gui/libraryproperties/constants/ConstantsPropertiesViewModelTest.java @@ -1,6 +1,7 @@ package org.jabref.gui.libraryproperties.constants; import java.util.List; +import java.util.stream.Stream; import javafx.beans.property.StringProperty; @@ -70,4 +71,65 @@ void stringsListPropertyResorting() { assertEquals(expected, actual); } + + @Test + @DisplayName("Check that the storeSettings method store settings on the model") + void storeSettingsTest() { + // Setup + // create a bibdatabse + BibDatabase db = new BibDatabase(); + BibDatabaseContext context = new BibDatabaseContext(db); + List expected = List.of("KTH", "Royal Institute of Technology"); + // initialize a constantsPropertiesViewModel + ConstantsPropertiesViewModel model = new ConstantsPropertiesViewModel(context, service, filePreferences); + + // construct value to store in model + var stringsList = model.stringsListProperty(); + stringsList.add(new ConstantsItemModel("KTH", "Royal Institute of Technology")); + + // Act + model.storeSettings(); + + // Assert + // get the names stored + List names = context.getDatabase().getStringValues().stream() + .map(BibtexString::getName).toList(); + // get the content stored + List content = context.getDatabase().getStringValues().stream() + .map(BibtexString::getContent).toList(); + + List actual = Stream.concat(names.stream(), content.stream()).toList(); + + assertEquals(expected, actual); + } + + @Test + @DisplayName("Check that the storeSettings method can identify string constants") + void storeSettingsWithStringConstantTest() { + // Setup + // create a bibdatabse + BibDatabase db = new BibDatabase(); + BibDatabaseContext context = new BibDatabaseContext(db); + List expected = List.of("@String{KTH = Royal Institute of Technology}"); + // initialize a constantsPropertiesViewModel + ConstantsPropertiesViewModel model = new ConstantsPropertiesViewModel(context, service, filePreferences); + + // construct value to store in model + var stringsList = model.stringsListProperty(); + stringsList.add(new ConstantsItemModel("KTH", "Royal Institute of Technology")); + + // Act + model.storeSettings(); + + // Assert + // get string the constants through parsedSerialization() method + List actual = context.getDatabase().getStringValues().stream() + .map(BibtexString::getParsedSerialization).toList(); + + // get the first value and clean strings + String actual_value = actual.getFirst().replaceAll("\\s+", " ").trim(); + String expected_value = expected.getFirst().replaceAll("\\s+", " ").trim(); + + assertEquals(expected_value, actual_value); + } } diff --git a/src/test/java/org/jabref/model/database/BibDatabaseTest.java b/src/test/java/org/jabref/model/database/BibDatabaseTest.java index 28e9db06298..e904015354a 100644 --- a/src/test/java/org/jabref/model/database/BibDatabaseTest.java +++ b/src/test/java/org/jabref/model/database/BibDatabaseTest.java @@ -392,8 +392,8 @@ void resolveForStringsOddHashMarkAtTheEnd() { @Test void getUsedStrings() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.AUTHOR, "#AAA#"); + BibEntry entry = new BibEntry() + .withField(StandardField.AUTHOR, "#AAA#"); BibtexString tripleA = new BibtexString("AAA", "Some other #BBB#"); BibtexString tripleB = new BibtexString("BBB", "Some more text"); BibtexString tripleC = new BibtexString("CCC", "Even more text"); @@ -412,8 +412,8 @@ void getUsedStrings() { @Test void getUsedStringsSingleString() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.AUTHOR, "#AAA#"); + BibEntry entry = new BibEntry() + .withField(StandardField.AUTHOR, "#AAA#"); BibtexString tripleA = new BibtexString("AAA", "Some other text"); BibtexString tripleB = new BibtexString("BBB", "Some more text"); List strings = new ArrayList<>(1); @@ -429,8 +429,8 @@ void getUsedStringsSingleString() { @Test void getUsedStringsNoString() { - BibEntry entry = new BibEntry(); - entry.setField(StandardField.AUTHOR, "Oscar Gustafsson"); + BibEntry entry = new BibEntry() + .withField(StandardField.AUTHOR, "Oscar Gustafsson"); BibtexString string = new BibtexString("AAA", "Some other text"); database.addString(string); database.insertEntry(entry);