add correct handling for keepOrWarn when moving taxa
[taxeditor.git] / eu.etaxonomy.taxeditor.navigation / src / main / java / eu / etaxonomy / taxeditor / navigation / navigator / e4 / TreeNodeDropAdapterE4.java
1 /**
2 * Copyright (C) 2015 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.taxeditor.navigation.navigator.e4;
10
11 import java.util.ArrayList;
12 import java.util.EnumSet;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Set;
17 import java.util.UUID;
18
19 import javax.inject.Inject;
20
21 import org.apache.log4j.Logger;
22 import org.eclipse.core.commands.operations.IUndoContext;
23 import org.eclipse.e4.ui.workbench.modeling.EPartService;
24 import org.eclipse.jface.dialogs.MessageDialog;
25 import org.eclipse.jface.util.LocalSelectionTransfer;
26 import org.eclipse.jface.viewers.ISelection;
27 import org.eclipse.jface.viewers.TreeSelection;
28 import org.eclipse.jface.viewers.ViewerDropAdapter;
29 import org.eclipse.swt.dnd.DND;
30 import org.eclipse.swt.dnd.DropTargetEvent;
31 import org.eclipse.swt.dnd.TransferData;
32 import org.eclipse.swt.widgets.Display;
33
34 import eu.etaxonomy.cdm.api.application.CdmApplicationState;
35 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
36 import eu.etaxonomy.cdm.api.service.UpdateResult;
37 import eu.etaxonomy.cdm.common.monitor.IRemotingProgressMonitor;
38 import eu.etaxonomy.cdm.model.metadata.SecReferenceHandlingEnum;
39 import eu.etaxonomy.cdm.model.permission.CRUD;
40 import eu.etaxonomy.cdm.model.reference.Reference;
41 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
42 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
43 import eu.etaxonomy.taxeditor.event.EventUtility;
44 import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
45 import eu.etaxonomy.taxeditor.model.AbstractUtility;
46 import eu.etaxonomy.taxeditor.model.MessagingUtils;
47 import eu.etaxonomy.taxeditor.navigation.l10n.Messages;
48 import eu.etaxonomy.taxeditor.operation.IPostMoniteredOperationEnabled;
49 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
50 import eu.etaxonomy.taxeditor.store.CdmStore;
51 import eu.etaxonomy.taxeditor.ui.dialog.selection.ReferenceSelectionDialog;
52
53 /**
54 * @author k.luther
55 * @date 02.06.2015
56 */
57 public class TreeNodeDropAdapterE4 extends ViewerDropAdapter implements IPostMoniteredOperationEnabled{
58
59 protected static final String TREE_NODE_DROP_ADAPTER_UNSAVED_PARENT_MESSAGE = Messages.TreeNodeDropAdapter_UNSAVED_PARENT_MESSAGE;
60 protected static final String TREE_NODE_DROP_ADAPTER_UNSAVED_PARENT = Messages.TreeNodeDropAdapter_UNSAVED_PARENT;
61 protected static final String TREE_NODE_DROP_ADAPTER_MOVE_TAXON = Messages.TreeNodeDropAdapter_MOVE_TAXON;
62 protected static final String TREE_NODE_DROP_ADAPTER_CANCEL = Messages.TreeNodeDropAdapter_CANCEL;
63 protected static final String TREE_NODE_DROP_ADAPTER_BEHIND = Messages.TreeNodeDropAdapter_BEHIND;
64 protected static final String TREE_NODE_DROP_ADAPTER_CHILD = Messages.TreeNodeDropAdapter_CHILD;
65 protected static final String DO_YOU_WANT_TO_MOVE_THE_TAXONNODE_AS_CHILD_OR_BEHIND_THE_TARGET_NODE = Messages.TreeNodeDropAdapter_MOVE_BEHIND;
66 protected static final String TARGET_NODE = Messages.TreeNodeDropAdapter_TARGET_NODE;
67
68 private static final Logger logger = Logger.getLogger(TreeNodeDropAdapterE4.class);
69
70 private TaxonNavigatorE4 taxonNavigator;
71 UpdateResult result;
72 TaxonNodeDto targetITaxonTreeNode;
73 HashSet<TaxonNodeDto> taxonNodes;
74 SecReferenceHandlingEnum secHandling;
75 UUID newSecUuid;
76 public static final String ID = "eu.etaxonomy.taxeditor.navigation.navigator.dropassistant"; //$NON-NLS-1$
77
78 private static final EnumSet<CRUD> UPDATE = EnumSet.of(CRUD.UPDATE);
79
80 @Inject
81 private EPartService partService;
82
83 public enum MovingType{
84 CHILD, PREVIOUS, BEHIND
85 }
86
87 protected TreeNodeDropAdapterE4(TaxonNavigatorE4 navigator) {
88 super(navigator.getViewer());
89 this.taxonNavigator = navigator;
90 }
91
92 @Override
93 public boolean performDrop(Object data) {
94 Object target = getCurrentTarget();
95 if (getCurrentTarget() instanceof TaxonNodeDto) {
96 Set<TaxonNodeDto> taxonNodes = getSelectedTaxa();
97 TaxonNodeDto targetTreeNode = (TaxonNodeDto) target;
98
99 if(taxonNodes != null) {
100 boolean success = moveTaxon(taxonNodes, targetTreeNode);
101 return success;
102 }
103 }
104 return false;
105 }
106
107 private Set<TaxonNodeDto> getSelectedTaxa(){
108 taxonNodes = new HashSet<>();
109
110 ISelection selection = LocalSelectionTransfer.getTransfer().getSelection();
111 if (selection instanceof TreeSelection) {
112
113 Iterator<?> selectionIterator = ((TreeSelection) selection).iterator();
114
115 while (selectionIterator.hasNext()){
116 Object object = selectionIterator.next();
117 if(object instanceof TaxonNodeDto){
118 TaxonNodeDto taxonNode = (TaxonNodeDto) object;
119 taxonNodes.add(taxonNode);
120 }
121 }
122 }
123 return taxonNodes;
124 }
125
126 @Override
127 public boolean validateDrop(Object target, int operation,
128 TransferData transferType) {
129
130 if (target instanceof TaxonNodeDto) {
131
132 // check users permissions with target taxonnode and taxon
133 if (target instanceof TaxonNodeDto) {
134 TaxonNodeDto targetNode = (TaxonNodeDto)target;
135 TaxonNode node = CdmStore.getService(ITaxonNodeService.class).load(targetNode.getUuid());
136 boolean hasTargetNodePermission = CdmStore.currentAuthentiationHasPermission(node, UPDATE);
137 boolean hasTargetTaxonPermission = node.getTaxon() == null ?
138 true :
139 CdmStore.currentAuthentiationHasPermission(node.getTaxon(), UPDATE);
140
141 if(!hasTargetNodePermission || ! hasTargetNodePermission){
142 if(logger.isDebugEnabled()){
143 logger.debug("CANCEL_STATUS for target node: " + hasTargetNodePermission + " " + hasTargetTaxonPermission + " "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
144 }
145 return false;
146 }
147 }
148
149 // do not allow to drop onto itself and
150 // check users permissions with all selected taxon nodes and taxa
151 List<UUID> nodeUuids = new ArrayList<>();
152 boolean isNotSameTaxonNode = true;
153 boolean isNotSameParent = true;
154 for(TaxonNodeDto taxonNode : getSelectedTaxa()){
155 if (logger.isDebugEnabled()){
156 logger.debug("selectedTaxa: " + (taxonNode.getTaxonUuid() == null? "-" : taxonNode.getTitleCache())); //$NON-NLS-1$
157 }
158 boolean isClassification = taxonNode.getTaxonUuid()== null;
159 if (isClassification) {
160 if(logger.isDebugEnabled()){
161 logger.debug("CANCEL_STATUS for selected classification " + taxonNode.getClassificationUUID()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
162 }
163 return false;
164 }
165 isNotSameTaxonNode = isNotSameTaxonNode && !taxonNode.equals(target);
166 isNotSameParent = isNotSameParent && !taxonNode.getParentUUID().equals(((TaxonNodeDto) target).getUuid());
167 nodeUuids.add(taxonNode.getUuid());
168
169 }
170 List<TaxonNode> nodes = CdmStore.getService(ITaxonNodeService.class).load(nodeUuids, null);
171 boolean hasPermission = true;
172 for (TaxonNode node: nodes){
173 hasPermission = hasPermission && CdmStore.currentAuthentiationHasPermission(node, UPDATE) && CdmStore.currentAuthentiationHasPermission(node.getTaxon(), UPDATE);
174 }
175
176 if (
177 !isNotSameTaxonNode
178 || !isNotSameParent
179 || !hasPermission
180
181 ) {
182 if(logger.isDebugEnabled()){
183 logger.debug("CANCEL_STATUS for selected " + isNotSameTaxonNode + Messages.TreeNodeDropAdapter_10 + hasPermission + " "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
184 }
185 return false;
186 }
187 }
188 logger.debug("OK_STATUS"); //$NON-NLS-1$
189 return true;
190 }
191
192 private boolean moveTaxon(Set<TaxonNodeDto> taxonNodes, TaxonNodeDto targetITaxonTreeNode) {
193 Iterator<TaxonNodeDto> taxIterator = taxonNodes.iterator();
194 this.targetITaxonTreeNode = targetITaxonTreeNode;
195 UUID parentSecUuid = targetITaxonTreeNode.getSecUuid();
196 Set<UUID> uuids = new HashSet<UUID>();
197 TaxonNodeDto node = null;
198 boolean targetIsPublish = targetITaxonTreeNode.isPublish();
199 boolean isPublishEqual = true;
200 Set<UUID> secReferences = new HashSet<>();
201 while(taxIterator.hasNext()){
202 node = taxIterator.next();
203 uuids.add(node.getUuid());
204 isPublishEqual &= targetIsPublish == node.isPublish();
205 secReferences.add(node.getSecUuid());
206 }
207
208 if (!isPublishEqual){
209 MessageDialog.openWarning(null, "Publish status differ", Messages.RemotingChangeAcceptedTaxonToSynonym_warning_publish);
210 }
211 secHandling = PreferencesUtil.getSecReferenceHandlingPreference();
212 newSecUuid = null;
213 if ( secHandling.equals(SecReferenceHandlingEnum.AlwaysSelect) || (secReferences.size() > 1 && secHandling.equals(SecReferenceHandlingEnum.KeepOrWarn) || secHandling.equals(SecReferenceHandlingEnum.KeepOrSelect)) || (secReferences.size() == 1 && (secHandling.equals(SecReferenceHandlingEnum.KeepOrWarn) ||secHandling.equals(SecReferenceHandlingEnum.KeepOrSelect)) && !secReferences.contains(parentSecUuid))){
214 //The moved nodes have different secundum references
215 String message = null;
216 String[] options = null;
217 if (secHandling.equals(SecReferenceHandlingEnum.AlwaysSelect) && (parentSecUuid != null && secReferences.contains(parentSecUuid))){
218 message = Messages.TreeNodeDropAdapter_Select_Sec_Reference_Handling_message_always_select;
219 options = new String[]{Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Keep, Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Select};
220 }else {
221 message = Messages.TreeNodeDropAdapter_Select_Sec_Reference_Handling_message;
222 options = new String[]{Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Keep, Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Select, Messages.TreeNodeDropAdapter_Select_Sec_Reference_Parent, };
223 }
224 int result = 0;
225 if (secHandling.equals(SecReferenceHandlingEnum.KeepOrWarn) ){
226 MessageDialog.openWarning(null, "Secundum references differ", Messages.MoveTaxon_Different_Secundum_References);
227 }else{
228 result = MessagingUtils.confirmDialog(Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Handling_title, message, options);
229 }
230
231 // int result = MessagingUtils.confirmDialog(Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Handling_title, Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Handling_message,
232 // new String[]{Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Keep, Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Parent, Messages.RemotingChangeAcceptedTaxonToSynonymHandler_Select_Sec_Reference_Select});
233 if (result == 1){
234 //select new reference
235 Reference sec = ReferenceSelectionDialog.select(AbstractUtility.getShell(), null);
236 newSecUuid = sec != null? sec.getUuid(): null;
237 }else if (result == 2){
238 //use parent sec
239 secHandling = SecReferenceHandlingEnum.UseNewParentSec;
240 newSecUuid = parentSecUuid;
241 }else if (result == 0){
242 //keep sec (also homotypic synonyms with different sec will keep the secundum)
243 secHandling = SecReferenceHandlingEnum.KeepOrWarn;
244 }else{
245 return false;
246 }
247 }
248
249 IUndoContext workspaceUndoContext = taxonNavigator.getUndoContext();
250 int movingTypeInt = 0;
251 if (PreferencesUtil.isNodesSortedNaturally()){
252 String[] buttonLables = {TREE_NODE_DROP_ADAPTER_CHILD, TREE_NODE_DROP_ADAPTER_BEHIND,TREE_NODE_DROP_ADAPTER_CANCEL};
253 MessageDialog dialog = new MessageDialog(null, TARGET_NODE, null, DO_YOU_WANT_TO_MOVE_THE_TAXONNODE_AS_CHILD_OR_BEHIND_THE_TARGET_NODE, MessageDialog.QUESTION_WITH_CANCEL, buttonLables, 0);
254 dialog.open();
255 int returnCode = dialog.getReturnCode();
256
257 if (returnCode == 0){
258 if (workspaceUndoContext == null) {
259 logger.error("Workspace undo context is null. DND operation cancelled"); //$NON-NLS-1$
260 return false;
261 }
262
263 }else if (returnCode == 1){
264 if (workspaceUndoContext == null) {
265 logger.error("Workspace undo context is null. DND operation cancelled"); //$NON-NLS-1$
266 return false;
267 }
268 movingTypeInt = 2;
269
270 } else {
271 return false;
272 }
273 }
274 moveNodes(uuids, targetITaxonTreeNode.getUuid(), movingTypeInt);
275 return true;
276 }
277
278 private void moveNodes(Set<UUID> taxonNodesToMoveUuid, UUID newParentTreeNodeUuid, int movingTypeInt){
279 UUID uuid = CdmApplicationState.getLongRunningTasksService().monitLongRunningTask(taxonNodesToMoveUuid,
280 newParentTreeNodeUuid, movingTypeInt, secHandling, newSecUuid);
281
282 Display.getDefault().asyncExec(new Runnable() {
283 @Override
284 public void run() {
285 AbstractUtility.executeMoniteredOperation("Move Taxon to new parent: ",
286 uuid,
287 1000,
288 false,
289 TreeNodeDropAdapterE4.this,
290 null,
291 false,
292 true);
293 }
294 });
295 }
296
297 @Override
298 public void dragOver(DropTargetEvent event) {
299 super.dragOver(event);
300 event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_INSERT_AFTER | DND.FEEDBACK_SCROLL;
301 }
302
303 @Override
304 public void postOperation(IRemotingProgressMonitor monitor) {
305 EventUtility.postEvent(WorkbenchEventConstants.REFRESH_NAVIGATOR, targetITaxonTreeNode);
306 taxonNodes.forEach(nodeDto->EventUtility.postEvent(WorkbenchEventConstants.REFRESH_NAME_EDITOR, nodeDto.getTaxonUuid()));
307 EventUtility.postEvent(WorkbenchEventConstants.REFRESH_NAME_EDITOR, targetITaxonTreeNode.getTaxonUuid());
308
309 }
310 }