Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check group type before showing dialog in edit group #8909

Merged
merged 30 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4160f51
Fix #8189 by checking group type
LIM0000 Jun 19, 2022
ba4e486
Update different solution that is capable to prevent warning dialog w…
LIM0000 Jun 21, 2022
556e954
Split groupType checking and groupFields checking into 2 different fu…
LIM0000 Jun 24, 2022
a4643e8
Merge branch 'main' of https://github.com/LIM0000/jabref into fix-for…
LIM0000 Jun 24, 2022
33de3cf
Merge remote-tracking branch 'upstream/main' into fix-for-issue-8189
Siedlerchr Jul 15, 2022
d84e892
Merge remote-tracking branch 'upstream/main' into fix-for-issue-8189
Siedlerchr Sep 4, 2022
eaac2e9
add check before dialog
Siedlerchr Sep 4, 2022
c7c085a
write groups always
Siedlerchr Sep 4, 2022
a618152
refersh
Siedlerchr Sep 4, 2022
56c1c86
store group changes
Siedlerchr Sep 4, 2022
e5b0c90
rfactor code
Siedlerchr Sep 4, 2022
e6a105c
refactor with intanceof
Siedlerchr Sep 4, 2022
3c1e409
refactor
Siedlerchr Sep 4, 2022
cea4e2b
fix stupid instanceof error^^
Siedlerchr Sep 4, 2022
69b03ec
checkstyle
Siedlerchr Sep 4, 2022
8f4849e
use getclass everywhere add comment
Siedlerchr Sep 4, 2022
b80d878
Change or to and, only return true if no changes
Siedlerchr Sep 4, 2022
ff18bb5
fuu checkstyle
Siedlerchr Sep 4, 2022
898e1fc
javadoc checkstyle
Siedlerchr Sep 4, 2022
1397fc0
Merge remote-tracking branch 'upstream/main' into fix-for-issue-8189
Siedlerchr Sep 10, 2022
9150938
TODO: clarifiy behavior when changing the groups name
Siedlerchr Sep 10, 2022
b81c1a5
Merge remote-tracking branch 'upstream/main' into fix-for-issue-8189
Siedlerchr Sep 12, 2022
78a3cd7
automatically assign when more than one group
Siedlerchr Sep 12, 2022
a1fad49
use new name
Siedlerchr Sep 12, 2022
860b749
todo
Siedlerchr Sep 14, 2022
5436087
Merge remote-tracking branch 'upstream/main' into fix-for-issue-8189
Siedlerchr Sep 17, 2022
b5a340c
we need to check old group name
Siedlerchr Sep 17, 2022
2d45980
checkstyle
Siedlerchr Sep 17, 2022
f068634
Merge remote-tracking branch 'upstream/main' into fix-for-issue-8189
Siedlerchr Oct 3, 2022
fa69ec7
Only check for minor mods if group types are equal
Siedlerchr Oct 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 130 additions & 27 deletions src/main/java/org/jabref/gui/groups/GroupTreeViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.groups.AbstractGroup;
import org.jabref.model.groups.AutomaticKeywordGroup;
import org.jabref.model.groups.AutomaticPersonsGroup;
import org.jabref.model.groups.ExplicitGroup;
import org.jabref.model.groups.GroupTreeNode;
import org.jabref.model.groups.RegexKeywordGroup;
import org.jabref.model.groups.SearchGroup;
import org.jabref.model.groups.TexGroup;
import org.jabref.model.groups.WordKeywordGroup;
import org.jabref.model.metadata.MetaData;
import org.jabref.preferences.PreferencesService;
Expand Down Expand Up @@ -145,14 +150,12 @@ private void onActiveDatabaseChanged(Optional<BibDatabaseContext> newDatabase) {
} else {
rootGroup.setValue(null);
}

currentDatabase = newDatabase;
}

/**
* Opens "New Group Dialog" and add the resulting group to the specified group
*/

public void addNewSubgroup(GroupNodeViewModel parent, GroupDialogHeader groupDialogHeader) {
currentDatabase.ifPresent(database -> {
Optional<AbstractGroup> newGroup = dialogService.showCustomDialogAndWait(new GroupDialogView(
Expand Down Expand Up @@ -182,61 +185,163 @@ private void writeGroupChangesToMetaData() {
currentDatabase.ifPresent(database -> database.getMetaData().setGroups(rootGroup.get().getGroupNode()));
}

private boolean isGroupTypeEqual(AbstractGroup oldGroup, AbstractGroup newGroup) {
return oldGroup.getClass().equals(newGroup.getClass());
}

/**
* Check if it is necessary to show a group modified, reassign entry dialog <br>
* Group name change is handled separately
*
* @param oldGroup Original Group
* @param newGroup Edited group
* @return true if just trivial modifications (e.g. color or description) or the relevant group properties are equal, false otherwise
*/
boolean onlyMinorChanges(AbstractGroup oldGroup, AbstractGroup newGroup) {
// we need to use getclass here because we have different subclass inheritance e.g. ExplicitGroup is a subclass of WordKeyWordGroup
if (oldGroup.getClass() == WordKeywordGroup.class) {
calixtus marked this conversation as resolved.
Show resolved Hide resolved
WordKeywordGroup oldWordKeywordGroup = (WordKeywordGroup) oldGroup;
WordKeywordGroup newWordKeywordGroup = (WordKeywordGroup) newGroup;

return Objects.equals(oldWordKeywordGroup.getSearchField().getName(), newWordKeywordGroup.getSearchField().getName())
&& Objects.equals(oldWordKeywordGroup.getSearchExpression(), newWordKeywordGroup.getSearchExpression())
&& Objects.equals(oldWordKeywordGroup.isCaseSensitive(), newWordKeywordGroup.isCaseSensitive());
} else if (oldGroup.getClass() == RegexKeywordGroup.class) {
RegexKeywordGroup oldRegexKeywordGroup = (RegexKeywordGroup) oldGroup;
RegexKeywordGroup newRegexKeywordGroup = (RegexKeywordGroup) newGroup;

return Objects.equals(oldRegexKeywordGroup.getSearchField().getName(), newRegexKeywordGroup.getSearchField().getName())
&& Objects.equals(oldRegexKeywordGroup.getSearchExpression(), newRegexKeywordGroup.getSearchExpression())
&& Objects.equals(oldRegexKeywordGroup.isCaseSensitive(), newRegexKeywordGroup.isCaseSensitive());
} else if ((oldGroup.getClass() == SearchGroup.class)) {
SearchGroup oldSearchGroup = (SearchGroup) oldGroup;
SearchGroup newSearchGroup = (SearchGroup) newGroup;

return Objects.equals(oldSearchGroup.getSearchExpression(), newSearchGroup.getSearchExpression())
&& Objects.equals(oldSearchGroup.getSearchFlags(), newSearchGroup.getSearchFlags());
} else if (oldGroup.getClass() == AutomaticKeywordGroup.class) {
AutomaticKeywordGroup oldAutomaticKeywordGroup = (AutomaticKeywordGroup) oldGroup;
AutomaticKeywordGroup newAutomaticKeywordGroup = (AutomaticKeywordGroup) oldGroup;

return Objects.equals(oldAutomaticKeywordGroup.getKeywordDelimiter(), newAutomaticKeywordGroup.getKeywordDelimiter())
&& Objects.equals(oldAutomaticKeywordGroup.getKeywordHierarchicalDelimiter(), newAutomaticKeywordGroup.getKeywordHierarchicalDelimiter())
&& Objects.equals(oldAutomaticKeywordGroup.getField().getName(), newAutomaticKeywordGroup.getField().getName());
} else if (oldGroup.getClass() == AutomaticPersonsGroup.class) {
AutomaticPersonsGroup oldAutomaticPersonsGroup = (AutomaticPersonsGroup) oldGroup;
AutomaticPersonsGroup newAutomaticPersonsGroup = (AutomaticPersonsGroup) newGroup;

return Objects.equals(oldAutomaticPersonsGroup.getField().getName(), newAutomaticPersonsGroup.getField().getName());
} else if (oldGroup.getClass() == TexGroup.class) {
TexGroup oldTexGroup = (TexGroup) oldGroup;
TexGroup newTexGroup = (TexGroup) newGroup;
return Objects.equals(oldTexGroup.getFilePath().toString(), newTexGroup.getFilePath().toString());
}
return true;
}

/**
* Opens "Edit Group Dialog" and changes the given group to the edited one.
*/
public void editGroup(GroupNodeViewModel oldGroup) {
currentDatabase.ifPresent(database -> {
Optional<AbstractGroup> newGroup = dialogService.showCustomDialogAndWait(new GroupDialogView(
dialogService,
database,
preferences,
oldGroup.getGroupNode().getGroup(),
GroupDialogHeader.SUBGROUP));

dialogService,
database,
preferences,
oldGroup.getGroupNode().getGroup(),
GroupDialogHeader.SUBGROUP));
newGroup.ifPresent(group -> {
// TODO: Keep assignments

AbstractGroup oldGroupDef = oldGroup.getGroupNode().getGroup();
String oldGroupName = oldGroupDef.getName();

boolean groupTypeEqual = isGroupTypeEqual(oldGroupDef, group);
boolean onlyMinorModifications = groupTypeEqual && onlyMinorChanges(oldGroupDef, group);

// dialog already warns us about this if the new group is named like another existing group
// We need to check if only the name changed as this is relevant for the entry's group field
if (groupTypeEqual && !group.getName().equals(oldGroupName) && onlyMinorModifications) {
int groupsWithSameName = 0;
Optional<GroupTreeNode> databaseRootGroup = currentDatabase.get().getMetaData().getGroups();
if (databaseRootGroup.isPresent()) {
// we need to check the old name for duplicates. If the new group name occurs more than once, it won't matter
groupsWithSameName = databaseRootGroup.get().findChildrenSatisfying(g -> g.getName().equals(oldGroupName)).size();
}
boolean removePreviousAssignments = true;
// We found more than 2 groups, so we cannot simply remove old assignment
if (groupsWithSameName >= 2) {
removePreviousAssignments = false;
}

oldGroup.getGroupNode().setGroup(
group,
true,
removePreviousAssignments,
database.getEntries());

dialogService.notify(Localization.lang("Modified group \"%0\".", group.getName()));
writeGroupChangesToMetaData();
// This is ugly but we have no proper update mechanism in place to propagate the changes, so redraw everything
refresh();
return;
}

if (groupTypeEqual && onlyMinorChanges(oldGroup.getGroupNode().getGroup(), group)) {
oldGroup.getGroupNode().setGroup(
group,
true,
true,
database.getEntries());

writeGroupChangesToMetaData();
refresh();
return;
}

// Major modifications

String content = Localization.lang("Assign the original group's entries to this group?");
ButtonType keepAssignments = new ButtonType(Localization.lang("Assign"), ButtonBar.ButtonData.YES);
ButtonType removeAssignments = new ButtonType(Localization.lang("Do not assign"), ButtonBar.ButtonData.NO);
ButtonType cancel = new ButtonType(Localization.lang("Cancel"), ButtonBar.ButtonData.CANCEL_CLOSE);

if (newGroup.get().getClass() == WordKeywordGroup.class) {
content = content + "\n\n" +
Localization.lang("(Note: If original entries lack keywords to qualify for the new group configuration, confirming here will add them)");
Localization.lang("(Note: If original entries lack keywords to qualify for the new group configuration, confirming here will add them)");
}
Optional<ButtonType> previousAssignments = dialogService.showCustomButtonDialogAndWait(Alert.AlertType.WARNING,
Localization.lang("Change of Grouping Method"),
content,
keepAssignments,
removeAssignments,
cancel);
// WarnAssignmentSideEffects.warnAssignmentSideEffects(newGroup, panel.frame());
Localization.lang("Change of Grouping Method"),
content,
keepAssignments,
removeAssignments,
cancel);
Siedlerchr marked this conversation as resolved.
Show resolved Hide resolved
boolean removePreviousAssignments = (oldGroup.getGroupNode().getGroup() instanceof ExplicitGroup)
&& (group instanceof ExplicitGroup);
&& (group instanceof ExplicitGroup);

int groupsWithSameName = 0;
Optional<GroupTreeNode> databaseRootGroup = currentDatabase.get().getMetaData().getGroups();
if (databaseRootGroup.isPresent()) {
String name = oldGroup.getGroupNode().getGroup().getName();
groupsWithSameName = databaseRootGroup.get().findChildrenSatisfying(g -> g.getName().equals(name)).size();
}
// okay we found more than 2 groups with the same name
// If we only found one we can still do it
if (groupsWithSameName >= 2) {
removePreviousAssignments = false;
}

if (previousAssignments.isPresent() && (previousAssignments.get().getButtonData() == ButtonBar.ButtonData.YES)) {
oldGroup.getGroupNode().setGroup(
group,
true,
removePreviousAssignments,
database.getEntries());
group,
true,
removePreviousAssignments,
database.getEntries());
} else if (previousAssignments.isPresent() && (previousAssignments.get().getButtonData() == ButtonBar.ButtonData.NO)) {
oldGroup.getGroupNode().setGroup(
group,
false,
removePreviousAssignments,
database.getEntries());
group,
false,
removePreviousAssignments,
database.getEntries());
} else if (previousAssignments.isPresent() && (previousAssignments.get().getButtonData() == ButtonBar.ButtonData.CANCEL_CLOSE)) {
return;
}
Expand All @@ -259,10 +364,8 @@ public void editGroup(GroupNodeViewModel oldGroup) {
// if (!addChange.isEmpty()) {
// undoAddPreviousEntries = UndoableChangeEntriesOfGroup.getUndoableEdit(null, addChange);
// }

dialogService.notify(Localization.lang("Modified group \"%0\".", group.getName()));
writeGroupChangesToMetaData();

// This is ugly but we have no proper update mechanism in place to propagate the changes, so redraw everything
refresh();
});
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/jabref/model/groups/GroupTreeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ public List<FieldChange> setGroup(AbstractGroup newGroup, boolean shouldKeepPrev
group = Objects.requireNonNull(newGroup);

List<FieldChange> changes = new ArrayList<>();
boolean shouldRemove = shouldRemovePreviousAssignments && (oldGroup instanceof GroupEntryChanger);
boolean shouldAdd = shouldKeepPreviousAssignments && (newGroup instanceof GroupEntryChanger);
if (shouldAdd || shouldRemove) {
boolean shouldRemoveFromOldGroup = shouldRemovePreviousAssignments && (oldGroup instanceof GroupEntryChanger);
boolean shouldAddToNewGroup = shouldKeepPreviousAssignments && (newGroup instanceof GroupEntryChanger);
if (shouldAddToNewGroup || shouldRemoveFromOldGroup) {
List<BibEntry> entriesMatchedByOldGroup = entriesInDatabase.stream().filter(oldGroup::isMatch)
.collect(Collectors.toList());
if (shouldRemove) {
if (shouldRemoveFromOldGroup) {
GroupEntryChanger entryChanger = (GroupEntryChanger) oldGroup;
changes.addAll(entryChanger.remove(entriesMatchedByOldGroup));
}

if (shouldAdd) {
if (shouldAddToNewGroup) {
GroupEntryChanger entryChanger = (GroupEntryChanger) newGroup;
changes.addAll(entryChanger.add(entriesMatchedByOldGroup));
}
Expand Down
64 changes: 59 additions & 5 deletions src/test/java/org/jabref/gui/groups/GroupTreeViewModelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.groups.AbstractGroup;
import org.jabref.model.groups.AllEntriesGroup;
import org.jabref.model.groups.ExplicitGroup;
import org.jabref.model.groups.GroupHierarchyType;
Expand All @@ -20,17 +21,22 @@

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Answers;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class GroupTreeViewModelTest {

private StateManager stateManager;
private GroupTreeViewModel groupTree;
private BibDatabaseContext databaseContext;
private TaskExecutor taskExecutor;
private PreferencesService preferencesService;
private DialogService dialogService;

@BeforeEach
void setUp() {
Expand All @@ -39,12 +45,13 @@ void setUp() {
stateManager.activeDatabaseProperty().setValue(Optional.of(databaseContext));
taskExecutor = new CurrentThreadTaskExecutor();
preferencesService = mock(PreferencesService.class);
dialogService = mock(DialogService.class, Answers.RETURNS_DEEP_STUBS);

when(preferencesService.getGroupsPreferences()).thenReturn(new GroupsPreferences(
GroupViewMode.UNION,
true,
true,
new SimpleObjectProperty<>(',')
));
GroupViewMode.UNION,
true,
true,
new SimpleObjectProperty<>(',')));
Siedlerchr marked this conversation as resolved.
Show resolved Hide resolved
groupTree = new GroupTreeViewModel(stateManager, mock(DialogService.class), preferencesService, taskExecutor, new CustomLocalDragboard());
}

Expand Down Expand Up @@ -85,4 +92,51 @@ void keywordGroupsAreNotRemovedFromEntriesOnDelete() {

assertEquals(groupName, entry.getField(StandardField.KEYWORDS).get());
}

@Test
void shouldNotShowDialogWhenGroupNameChanges() {
AbstractGroup oldGroup = new ExplicitGroup("group", GroupHierarchyType.INDEPENDENT, ',');
AbstractGroup newGroup = new ExplicitGroup("newGroupName", GroupHierarchyType.INDEPENDENT, ',');
BibEntry entry = new BibEntry();
databaseContext.getDatabase().insertEntry(entry);

GroupTreeViewModel model = new GroupTreeViewModel(stateManager, dialogService, preferencesService, taskExecutor, new CustomLocalDragboard());
assertTrue(model.onlyMinorChanges(oldGroup, newGroup));
}

@Test
void shouldNotShowDialogWhenGroupsAreEqual() {
AbstractGroup oldGroup = new WordKeywordGroup("group", GroupHierarchyType.INCLUDING, StandardField.KEYWORDS, "keywordTest", true, ',', true);
AbstractGroup newGroup = new WordKeywordGroup("group", GroupHierarchyType.INCLUDING, StandardField.KEYWORDS, "keywordTest", true, ',', true);

BibEntry entry = new BibEntry();
databaseContext.getDatabase().insertEntry(entry);

GroupTreeViewModel model = new GroupTreeViewModel(stateManager, dialogService, preferencesService, taskExecutor, new CustomLocalDragboard());
assertTrue(model.onlyMinorChanges(oldGroup, newGroup));
}

@Test
void shouldShowDialogWhenKeywordDiffers() {
AbstractGroup oldGroup = new WordKeywordGroup("group", GroupHierarchyType.INCLUDING, StandardField.KEYWORDS, "keywordTest", true, ',', true);
AbstractGroup newGroup = new WordKeywordGroup("group", GroupHierarchyType.INCLUDING, StandardField.KEYWORDS, "keywordChanged", true, ',', true);

BibEntry entry = new BibEntry();
databaseContext.getDatabase().insertEntry(entry);

GroupTreeViewModel model = new GroupTreeViewModel(stateManager, dialogService, preferencesService, taskExecutor, new CustomLocalDragboard());
assertFalse(model.onlyMinorChanges(oldGroup, newGroup));
}

@Test
void shouldShowDialogWhenCaseSensitivyDiffers() {
AbstractGroup oldGroup = new WordKeywordGroup("group", GroupHierarchyType.INCLUDING, StandardField.KEYWORDS, "keywordTest", false, ',', true);
AbstractGroup newGroup = new WordKeywordGroup("group", GroupHierarchyType.INCLUDING, StandardField.KEYWORDS, "keywordChanged", true, ',', true);

BibEntry entry = new BibEntry();
databaseContext.getDatabase().insertEntry(entry);

GroupTreeViewModel model = new GroupTreeViewModel(stateManager, dialogService, preferencesService, taskExecutor, new CustomLocalDragboard());
assertFalse(model.onlyMinorChanges(oldGroup, newGroup));
}
}