diff --git a/src/main/java/org/jabref/gui/EntryTypeViewModel.java b/src/main/java/org/jabref/gui/EntryTypeViewModel.java index 2326c66cf7e..c8b11e5e1e7 100644 --- a/src/main/java/org/jabref/gui/EntryTypeViewModel.java +++ b/src/main/java/org/jabref/gui/EntryTypeViewModel.java @@ -16,8 +16,8 @@ import org.jabref.Globals; import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; -import org.jabref.logic.bibtex.DuplicateCheck; import org.jabref.logic.citationkeypattern.CitationKeyGenerator; +import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.IdBasedFetcher; import org.jabref.logic.importer.WebFetchers; diff --git a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java index eeb4528de9f..9f5c778e677 100644 --- a/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java +++ b/src/main/java/org/jabref/gui/duplicationFinder/DuplicateSearch.java @@ -25,7 +25,7 @@ import org.jabref.gui.undo.UndoableRemoveEntries; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; -import org.jabref.logic.bibtex.DuplicateCheck; +import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; diff --git a/src/main/java/org/jabref/gui/importer/ImportAction.java b/src/main/java/org/jabref/gui/importer/ImportAction.java index d49010cb6fc..2a607482fce 100644 --- a/src/main/java/org/jabref/gui/importer/ImportAction.java +++ b/src/main/java/org/jabref/gui/importer/ImportAction.java @@ -16,6 +16,7 @@ import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.TaskExecutor; +import org.jabref.logic.database.DatabaseMerger; import org.jabref.logic.importer.ImportException; import org.jabref.logic.importer.ImportFormatReader; import org.jabref.logic.importer.Importer; @@ -23,12 +24,6 @@ import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.UpdateField; import org.jabref.model.database.BibDatabase; -import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.BibtexString; -import org.jabref.model.groups.AllEntriesGroup; -import org.jabref.model.groups.ExplicitGroup; -import org.jabref.model.groups.GroupHierarchyType; -import org.jabref.model.metadata.ContentSelector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -135,55 +130,15 @@ private ParserResult mergeImportResults(List entries = parserResult.getDatabase().getEntries(); - resultDatabase.insertEntries(entries); + resultDatabase.insertEntries(parserResult.getDatabase().getEntries()); if (ImportFormatReader.BIBTEX_FORMAT.equals(importResult.format)) { // additional treatment of BibTeX - // merge into existing database - - // Merge strings - for (BibtexString bibtexString : parserResult.getDatabase().getStringValues()) { - String bibtexStringName = bibtexString.getName(); - if (resultDatabase.hasStringByName(bibtexStringName)) { - String importedContent = bibtexString.getContent(); - String existingContent = resultDatabase.getStringByName(bibtexStringName).get().getContent(); - if (!importedContent.equals(existingContent)) { - LOGGER.warn("String contents differ for {}: {} != {}", bibtexStringName, importedContent, existingContent); - // TODO: decide what to do here (in case the same string exits) - } - } else { - resultDatabase.addString(bibtexString); - } - } - - // Merge groups - // Adds the specified node as a child of the current root. The group contained in newGroups must not be of - // type AllEntriesGroup, since every tree has exactly one AllEntriesGroup (its root). The newGroups are - // inserted directly, i.e. they are not deepCopy()'d. - parserResult.getMetaData().getGroups().ifPresent(newGroups -> { - // ensure that there is always only one AllEntriesGroup in the resulting database - // "Rename" the AllEntriesGroup of the imported database to "Imported" - if (newGroups.getGroup() instanceof AllEntriesGroup) { - // create a dummy group - try { - // This will cause a bug if the group already exists - // There will be group where the two groups are merged - String newGroupName = importResult.parserResult.getFile().map(File::getName).orElse("unknown"); - ExplicitGroup group = new ExplicitGroup("Imported " + newGroupName, GroupHierarchyType.INDEPENDENT, - Globals.prefs.getKeywordDelimiter()); - newGroups.setGroup(group); - group.add(parserResult.getDatabase().getEntries()); - } catch (IllegalArgumentException e) { - LOGGER.error("Problem appending entries to group", e); - } - } - result.getMetaData().getGroups().ifPresent(newGroups::moveTo); - }); - - for (ContentSelector selector : parserResult.getMetaData().getContentSelectorList()) { - result.getMetaData().addContentSelector(selector); - } + new DatabaseMerger().mergeMetaData( + result.getMetaData(), + parserResult.getMetaData(), + importResult.parserResult.getFile().map(File::getName).orElse("unknown"), + parserResult.getDatabase().getEntries()); } // TODO: collect errors into ParserResult, because they are currently ignored (see caller of this method) } diff --git a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java index 2c12cd4cda4..01425f5aa47 100644 --- a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java +++ b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java @@ -1,5 +1,6 @@ package org.jabref.gui.importer; +import java.io.File; import java.util.List; import java.util.Optional; @@ -19,23 +20,16 @@ import org.jabref.gui.externalfiles.ImportHandler; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.fieldeditors.LinkedFileViewModel; -import org.jabref.gui.groups.GroupTreeNodeViewModel; -import org.jabref.gui.groups.UndoableAddOrRemoveGroup; -import org.jabref.gui.undo.NamedCompound; -import org.jabref.gui.undo.UndoableInsertEntries; -import org.jabref.gui.undo.UndoableInsertString; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; -import org.jabref.logic.bibtex.DuplicateCheck; +import org.jabref.logic.database.DatabaseMerger; +import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.importer.ParserResult; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.BibtexString; import org.jabref.model.entry.LinkedFile; -import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.metadata.FilePreferences; -import org.jabref.model.metadata.MetaData; import org.jabref.model.util.FileUpdateMonitor; import org.jabref.preferences.PreferencesService; @@ -145,58 +139,12 @@ public void importEntries(List entriesToImport, boolean shouldDownload } } - NamedCompound namedCompound = new NamedCompound(Localization.lang("Import file")); - namedCompound.addEdit(new UndoableInsertEntries(databaseContext.getDatabase(), entriesToImport)); + new DatabaseMerger().mergeStrings(databaseContext.getDatabase(), parserResult.getDatabase()); + new DatabaseMerger().mergeMetaData(databaseContext.getMetaData(), + parserResult.getMetaData(), + parserResult.getFile().map(File::getName).orElse("unknown"), + parserResult.getDatabase().getEntries()); - // merge strings into target database - for (BibtexString bibtexString : parserResult.getDatabase().getStringValues()) { - String bibtexStringName = bibtexString.getName(); - if (databaseContext.getDatabase().hasStringByName(bibtexStringName)) { - String importedContent = bibtexString.getContent(); - String existingContent = databaseContext.getDatabase().getStringByName(bibtexStringName).get().getContent(); - if (!importedContent.equals(existingContent)) { - LOGGER.warn("String contents differ for {}: {} != {}", bibtexStringName, importedContent, existingContent); - // TODO: decide what to do here (in case the same string exits) - } - } else { - databaseContext.getDatabase().addString(bibtexString); - // FIXME: this prevents this method to be moved to logic - we need to implement a new undo/redo data model - namedCompound.addEdit(new UndoableInsertString(databaseContext.getDatabase(), bibtexString)); - } - } - - // copy content selectors to target database - MetaData targetMetada = databaseContext.getMetaData(); - parserResult.getMetaData() - .getContentSelectorList() - .forEach(targetMetada::addContentSelector); - // TODO undo of content selectors (currently not implemented) - - // copy groups to target database - parserResult.getMetaData().getGroups().ifPresent( - newGroupsTreeNode -> { - if (targetMetada.getGroups().isPresent()) { - GroupTreeNode groupTreeNode = targetMetada.getGroups().get(); - newGroupsTreeNode.moveTo(groupTreeNode); - namedCompound.addEdit( - new UndoableAddOrRemoveGroup( - new GroupTreeNodeViewModel(groupTreeNode), - new GroupTreeNodeViewModel(newGroupsTreeNode), - UndoableAddOrRemoveGroup.ADD_NODE)); - } else { - // target does not contain any groups, so we can just use the new groups - targetMetada.setGroups(newGroupsTreeNode); - namedCompound.addEdit( - new UndoableAddOrRemoveGroup( - new GroupTreeNodeViewModel(newGroupsTreeNode), - new GroupTreeNodeViewModel(newGroupsTreeNode), - UndoableAddOrRemoveGroup.ADD_NODE)); - } - } - ); - - namedCompound.end(); - Globals.undoManager.addEdit(namedCompound); JabRefGUI.getMainFrame().getCurrentBasePanel().markBaseChanged(); } diff --git a/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java b/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java index e3435b7ac7b..44ff461ee73 100644 --- a/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java +++ b/src/main/java/org/jabref/logic/bibtex/comparator/BibDatabaseDiff.java @@ -7,7 +7,7 @@ import java.util.Optional; import java.util.Set; -import org.jabref.logic.bibtex.DuplicateCheck; +import org.jabref.logic.database.DuplicateCheck; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.StandardField; diff --git a/src/main/java/org/jabref/logic/database/DatabaseMerger.java b/src/main/java/org/jabref/logic/database/DatabaseMerger.java new file mode 100644 index 00000000000..dae09f8a591 --- /dev/null +++ b/src/main/java/org/jabref/logic/database/DatabaseMerger.java @@ -0,0 +1,134 @@ +package org.jabref.logic.database; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.jabref.model.database.BibDatabase; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.database.BibDatabaseModeDetection; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibEntryTypesManager; +import org.jabref.model.entry.BibtexString; +import org.jabref.model.groups.AllEntriesGroup; +import org.jabref.model.groups.ExplicitGroup; +import org.jabref.model.groups.GroupHierarchyType; +import org.jabref.model.metadata.ContentSelector; +import org.jabref.model.metadata.MetaData; +import org.jabref.preferences.JabRefPreferences; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DatabaseMerger { + + private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseMerger.class); + + /** + * Merges all entries and strings of the other database into the target database. Any duplicates are ignored. + * In case a string has a different content, it is added with a new unique name. + * The unique name is generated by suffix "_i", where i runs from 1 onwards. + * + * @param other The other databases that is merged into this database + */ + public synchronized void merge(BibDatabase target, BibDatabase other) { + mergeEntries(target, other); + mergeStrings(target, other); + } + + /** + * Merges all entries, strings, and metaData of the other database context into the target database context. Any duplicates are ignored. + * In case a string has a different content, it is added with a new unique name. + * The unique name is generated by suffix "_i", where i runs from 1 onwards. + * + * @param other The other databases that is merged into this database + */ + public synchronized void merge(BibDatabaseContext target, BibDatabaseContext other, String otherFileName) { + mergeEntries(target.getDatabase(), other.getDatabase()); + mergeStrings(target.getDatabase(), other.getDatabase()); + mergeMetaData(target.getMetaData(), other.getMetaData(), otherFileName, other.getEntries()); + } + + private void mergeEntries(BibDatabase target, BibDatabase other) { + DuplicateCheck duplicateCheck = new DuplicateCheck(new BibEntryTypesManager()); + List newEntries = other.getEntries().stream() + // Remove all entries that are already part of the database (duplicate) + .filter(entry -> duplicateCheck.containsDuplicate(target, entry, BibDatabaseModeDetection.inferMode(target)).isEmpty()) + .collect(Collectors.toList()); + target.insertEntries(newEntries); + } + + public void mergeStrings(BibDatabase target, BibDatabase other) { + for (BibtexString bibtexString : other.getStringValues()) { + String bibtexStringName = bibtexString.getName(); + if (target.hasStringByName(bibtexStringName)) { + String importedContent = bibtexString.getContent(); + String existingContent = target.getStringByName(bibtexStringName).get().getContent(); + if (!importedContent.equals(existingContent)) { + LOGGER.info("String contents differ for {}: {} != {}", bibtexStringName, importedContent, existingContent); + int suffix = 1; + String newName = bibtexStringName + "_" + suffix; + while (target.hasStringByName(newName)) { + suffix++; + newName = bibtexStringName + "_" + suffix; + } + BibtexString newBibtexString = new BibtexString(newName, importedContent); + // TODO undo/redo + target.addString(newBibtexString); + LOGGER.info("New string added: {} = {}", newBibtexString.getName(), newBibtexString.getContent()); + } + } else { + // TODO undo/redo + target.addString(bibtexString); + } + } + } + + /** + * @param target the metaData that is the merge target + * @param other the metaData to merge into the target + * @param otherFilename the filename of the other library. Pass "unknown" if not known. + */ + public void mergeMetaData(MetaData target, MetaData other, String otherFilename, List allOtherEntries) { + Objects.requireNonNull(other); + Objects.requireNonNull(otherFilename); + Objects.requireNonNull(allOtherEntries); + + mergeGroups(target, other, otherFilename, allOtherEntries); + mergeContentSelectors(target, other); + } + + private void mergeGroups(MetaData target, MetaData other, String otherFilename, List allOtherEntries) { + // Adds the specified node as a child of the current root. The group contained in newGroups must not be of + // type AllEntriesGroup, since every tree has exactly one AllEntriesGroup (its root). The newGroups are + // inserted directly, i.e. they are not deepCopy()'d. + other.getGroups().ifPresent(newGroups -> { + // ensure that there is always only one AllEntriesGroup in the resulting database + // "Rename" the AllEntriesGroup of the imported database to "Imported" + if (newGroups.getGroup() instanceof AllEntriesGroup) { + // create a dummy group + try { + // This will cause a bug if the group already exists + // There will be group where the two groups are merged + String newGroupName = otherFilename; + ExplicitGroup group = new ExplicitGroup("Imported " + newGroupName, GroupHierarchyType.INDEPENDENT, + JabRefPreferences.getInstance().getKeywordDelimiter()); + newGroups.setGroup(group); + group.add(allOtherEntries); + } catch (IllegalArgumentException e) { + LOGGER.error("Problem appending entries to group", e); + } + } + target.getGroups().ifPresentOrElse( + newGroups::moveTo, + // target does not contain any groups, so we can just use the new groups + () -> target.setGroups(newGroups)); + }); + } + + private void mergeContentSelectors(MetaData target, MetaData other) { + for (ContentSelector selector : other.getContentSelectorList()) { + target.addContentSelector(selector); + } + } +} diff --git a/src/main/java/org/jabref/logic/bibtex/DuplicateCheck.java b/src/main/java/org/jabref/logic/database/DuplicateCheck.java similarity index 99% rename from src/main/java/org/jabref/logic/bibtex/DuplicateCheck.java rename to src/main/java/org/jabref/logic/database/DuplicateCheck.java index d601b92a41f..ec82b223d4d 100644 --- a/src/main/java/org/jabref/logic/bibtex/DuplicateCheck.java +++ b/src/main/java/org/jabref/logic/database/DuplicateCheck.java @@ -1,4 +1,4 @@ -package org.jabref.logic.bibtex; +package org.jabref.logic.database; import java.util.Collection; import java.util.HashMap; diff --git a/src/main/java/org/jabref/model/database/BibDatabase.java b/src/main/java/org/jabref/model/database/BibDatabase.java index 7ba57bf2efc..d609f5a9b8a 100644 --- a/src/main/java/org/jabref/model/database/BibDatabase.java +++ b/src/main/java/org/jabref/model/database/BibDatabase.java @@ -548,9 +548,9 @@ public void setEpilog(String epilog) { * Registers an listener object (subscriber) to the internal event bus. * The following events are posted: * - * - {@link EntryAddedEvent} - * - {@link EntryChangedEvent} - * - {@link EntriesRemovedEvent} + * - {@link EntriesAddedEvent} + * - {@link EntryChangedEvent} + * - {@link EntriesRemovedEvent} * * @param listener listener (subscriber) to add */ diff --git a/src/main/java/org/jabref/model/metadata/MetaData.java b/src/main/java/org/jabref/model/metadata/MetaData.java index c76442373fc..e1c409a22e8 100644 --- a/src/main/java/org/jabref/model/metadata/MetaData.java +++ b/src/main/java/org/jabref/model/metadata/MetaData.java @@ -22,6 +22,8 @@ import org.jabref.model.metadata.event.MetaDataChangedEvent; import com.google.common.eventbus.EventBus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MetaData { @@ -41,6 +43,8 @@ public class MetaData { public static final char SEPARATOR_CHARACTER = ';'; public static final String SEPARATOR_STRING = String.valueOf(SEPARATOR_CHARACTER); + private static final Logger LOGGER = LoggerFactory.getLogger(MetaData.class); + private final EventBus eventBus = new EventBus(); private final Map citeKeyPatterns = new HashMap<>(); // private final Map userFileDirectory = new HashMap<>(); // @@ -266,7 +270,7 @@ public void setEncoding(Charset encoding) { } /** - * This Method (with additional parameter) has been introduced to avoid event loops while saving a database. + * This method (with additional parameter) has been introduced to avoid event loops while saving a database. */ public void setEncoding(Charset encoding, ChangePropagation postChanges) { this.encoding = Objects.requireNonNull(encoding); diff --git a/src/test/java/org/jabref/logic/database/DatabaseMergerTest.java b/src/test/java/org/jabref/logic/database/DatabaseMergerTest.java new file mode 100644 index 00000000000..fa56cb9324d --- /dev/null +++ b/src/test/java/org/jabref/logic/database/DatabaseMergerTest.java @@ -0,0 +1,178 @@ +package org.jabref.logic.database; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jabref.model.database.BibDatabase; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibtexString; +import org.jabref.model.entry.field.StandardField; +import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.model.groups.AbstractGroup; +import org.jabref.model.groups.AllEntriesGroup; +import org.jabref.model.groups.ExplicitGroup; +import org.jabref.model.groups.GroupHierarchyType; +import org.jabref.model.groups.GroupTreeNode; +import org.jabref.model.metadata.ContentSelector; +import org.jabref.model.metadata.MetaData; +import org.jabref.preferences.JabRefPreferences; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class DatabaseMergerTest { + + @Test + void mergeAddsNonDuplicateEntries() { + // Entries 1 and 2 are identical + BibEntry entry1 = new BibEntry() + .withField(StandardField.AUTHOR, "Phillip Kaye and Michele Mosca") + .withField(StandardField.TITLE, "Quantum Networks for Generating Arbitrary Quantum States"); + entry1.setType(StandardEntryType.Article); + BibEntry entry2 = new BibEntry() + .withField(StandardField.AUTHOR, "Phillip Kaye and Michele Mosca") + .withField(StandardField.TITLE, "Quantum Networks for Generating Arbitrary Quantum States"); + entry2.setType(StandardEntryType.Article); + BibEntry entry3 = new BibEntry() + .withField(StandardField.AUTHOR, "Stephen Blaha") + .withField(StandardField.TITLE, "Quantum Computers and Quantum Computer Languages: Quantum Assembly Language and Quantum C Language"); + entry3.setType(StandardEntryType.Article); + + BibDatabase database = new BibDatabase(List.of(entry1)); + BibDatabase other = new BibDatabase(List.of(entry2, entry3)); + new DatabaseMerger().merge(database, other); + + assertEquals(2, database.getEntries().size()); + assertEquals(List.of(entry1, entry3), database.getEntries()); + } + + @Test + void mergeBibTexStringsWithSameNameAreImportedWithModifiedName() { + BibtexString targetString = new BibtexString("name", "content1"); + + // BibTeXStrings that are imported from two sources (same name different content) + BibtexString sourceString1 = new BibtexString("name", "content2"); + BibtexString sourceString2 = new BibtexString("name", "content3"); + + // The expected source BibTeXStrings after import (different name, different content) + BibtexString importedBibTeXString1 = new BibtexString("name_1", "content2"); + BibtexString importedBibTeXString2 = new BibtexString("name_2", "content3"); + + BibDatabase target = new BibDatabase(); + BibDatabase source1 = new BibDatabase(); + BibDatabase source2 = new BibDatabase(); + target.addString(targetString); + source1.addString(sourceString1); + source2.addString(sourceString2); + + new DatabaseMerger().mergeStrings(target, source1); + new DatabaseMerger().mergeStrings(target, source2); + // Use string representation to compare since the id will not match + List resultStringsSorted = target.getStringValues() + .stream() + .map(BibtexString::toString) + .sorted() + .collect(Collectors.toList()); + + assertEquals(List.of(targetString.toString(), importedBibTeXString1.toString(), + importedBibTeXString2.toString()), resultStringsSorted); + } + + @Test + void mergeBibTexStringsWithSameNameAndContentAreIgnored() { + BibtexString targetString1 = new BibtexString("name1", "content1"); + BibtexString targetString2 = new BibtexString("name2", "content2"); + + // BibTeXStrings that are imported (equivalent to target strings) + BibtexString sourceString1 = new BibtexString("name1", "content1"); + BibtexString sourceString2 = new BibtexString("name2", "content2"); + + BibDatabase target = new BibDatabase(); + BibDatabase source = new BibDatabase(); + target.addString(targetString1); + target.addString(targetString2); + source.addString(sourceString1); + source.addString(sourceString2); + + new DatabaseMerger().mergeStrings(target, source); + // Use string representation to compare since the id will not match + List resultStringsSorted = target.getStringValues() + .stream() + .map(BibtexString::toString) + .sorted() + .collect(Collectors.toList()); + + assertEquals(List.of(targetString1.toString(), targetString2.toString()), resultStringsSorted); + } + + @Test + void mergeMetaDataWithoutAllEntriesGroup() { + MetaData target = new MetaData(); + target.addContentSelector(new ContentSelector(StandardField.AUTHOR, List.of("Test Author"))); + GroupTreeNode targetRootGroup = new GroupTreeNode(new TestGroup("targetGroup", GroupHierarchyType.INDEPENDENT)); + target.setGroups(targetRootGroup); + MetaData other = new MetaData(); + GroupTreeNode otherRootGroup = new GroupTreeNode(new TestGroup("otherGroup", GroupHierarchyType.INCLUDING)); + other.setGroups(otherRootGroup); + other.addContentSelector(new ContentSelector(StandardField.TITLE, List.of("Test Title"))); + List expectedContentSelectors = + List.of(new ContentSelector(StandardField.AUTHOR, List.of("Test Author")), + new ContentSelector(StandardField.TITLE, List.of("Test Title"))); + + new DatabaseMerger().mergeMetaData(target, other, "unknown", List.of()); + + // Assert that content selectors are all merged + assertEquals(expectedContentSelectors, target.getContentSelectorList()); + + // Assert that groups of other are children of root node of target + assertEquals(targetRootGroup, target.getGroups().get()); + assertEquals(target.getGroups().get().getChildren().size(), 1); + assertEquals(otherRootGroup, target.getGroups().get().getChildren().get(0)); + } + + @Test + void mergeMetaDataWithAllEntriesGroup() { + MetaData target = new MetaData(); + target.addContentSelector(new ContentSelector(StandardField.AUTHOR, List.of("Test Author"))); + GroupTreeNode targetRootGroup = new GroupTreeNode(new AllEntriesGroup("targetGroup")); + target.setGroups(targetRootGroup); + MetaData other = new MetaData(); + GroupTreeNode otherRootGroup = new GroupTreeNode(new AllEntriesGroup("otherGroup")); + other.setGroups(otherRootGroup); + other.addContentSelector(new ContentSelector(StandardField.TITLE, List.of("Test Title"))); + List expectedContentSelectors = + List.of(new ContentSelector(StandardField.AUTHOR, List.of("Test Author")), + new ContentSelector(StandardField.TITLE, List.of("Test Title"))); + GroupTreeNode expectedImportedGroupNode = new GroupTreeNode(new ExplicitGroup("Imported unknown", GroupHierarchyType.INDEPENDENT, JabRefPreferences.getInstance().getKeywordDelimiter())); + + new DatabaseMerger().mergeMetaData(target, other, "unknown", List.of()); + + // Assert that groups of other are children of root node of target + assertEquals(targetRootGroup, target.getGroups().get()); + assertEquals(target.getGroups().get().getChildren().size(), 1); + assertEquals(expectedImportedGroupNode, target.getGroups().get().getChildren().get(0)); + } + + static class TestGroup extends AbstractGroup { + + protected TestGroup(String name, GroupHierarchyType context) { + super(name, context); + } + + @Override + public boolean contains(BibEntry entry) { + return false; + } + + @Override + public boolean isDynamic() { + return false; + } + + @Override + public AbstractGroup deepCopy() { + return null; + } + } +} diff --git a/src/test/java/org/jabref/logic/bibtex/DuplicateCheckTest.java b/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java similarity index 99% rename from src/test/java/org/jabref/logic/bibtex/DuplicateCheckTest.java rename to src/test/java/org/jabref/logic/database/DuplicateCheckTest.java index a4dddfacea5..6e76c89d14f 100644 --- a/src/test/java/org/jabref/logic/bibtex/DuplicateCheckTest.java +++ b/src/test/java/org/jabref/logic/database/DuplicateCheckTest.java @@ -1,4 +1,4 @@ -package org.jabref.logic.bibtex; +package org.jabref.logic.database; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.entry.BibEntry;