Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 2b0ddf98a55a04cefc154ad9d2ca91a74817c8a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/*****************************************************************************
 * Copyright (c) 2011-2012 CEA LIST.
 *   
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * 
 *  Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation
 *  Emilien Perico (Atos Origin) emilien.perico@atosorigin.com - refactor common behavior between diagrams
 *  
 *  CEA LIST - Adapted to use a local graphical type registry.
 *  
 *****************************************************************************/
package org.eclipse.papyrus.gmf.diagram.common.edit.policy;

import static org.eclipse.papyrus.gmf.diagram.common.provider.IGraphicalTypeRegistry.UNDEFINED_TYPE;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.commands.UnexecutableCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.DiagramDragDropEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.requests.ArrangeRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor;
import org.eclipse.gmf.runtime.diagram.ui.requests.DropObjectsRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.RefreshConnectionsRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.commands.wrappers.CommandProxyWithResult;
import org.eclipse.papyrus.gmf.diagram.common.provider.IGraphicalTypeRegistry;
import org.eclipse.papyrus.infra.gmfdiag.common.utils.ViewDescriptorUtil;
import org.eclipse.papyrus.uml.diagram.common.commands.CommonDeferredCreateConnectionViewCommand;
import org.eclipse.papyrus.uml.diagram.common.commands.SemanticAdapter;
import org.eclipse.papyrus.uml.diagram.common.edit.part.AbstractElementBorderEditPart;
import org.eclipse.papyrus.uml.diagram.common.edit.part.AbstractElementLabelEditPart;
import org.eclipse.papyrus.uml.diagram.common.util.CrossReferencerUtil;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;

/**
 * Abstract DND edit policy delegating the choice of the view to create for an EObject to a local
 * graphical type registry.
 */
public abstract class CommonDiagramDragDropEditPolicy extends DiagramDragDropEditPolicy {

	/** The graphical type registry. */
	protected IGraphicalTypeRegistry registry;

	/** The list of element types that require a specific drop command. */
	private Set<String> specificDropList = null;

	/** The specified link mapping helper depending on the diagram */
	protected ILinkMappingHelper linkMappingHelper;

	/** Constructor */
	public CommonDiagramDragDropEditPolicy(ILinkMappingHelper linkMappingHelper) {
		this.linkMappingHelper = linkMappingHelper;
	}

	private Set<String> getSpecificDropList() {
		if(specificDropList == null) {
			specificDropList = getSpecificDropBehaviorTypes();
		}
		return specificDropList;
	}

	protected abstract Set<String> getSpecificDropBehaviorTypes();

	/**
	 * <pre>
	 * {@inheritedDoc}.
	 * 
	 * Overridden method to fix some exception occurring while moving affixed element (nodes or labels)
	 * (https://bugs.eclipse.org/bugs/show_bug.cgi?id=350680).
	 * </pre>
	 */
	@Override
	protected Command getDropCommand(ChangeBoundsRequest request) {

		Iterator<?> iter = request.getEditParts().iterator();
		EObject graphicalParentObject = ((GraphicalEditPart)getHost()).resolveSemanticElement();
		while((graphicalParentObject != null) && (iter.hasNext())) {
			EditPart droppedEditPart = (EditPart)iter.next();
			if(droppedEditPart instanceof AbstractElementBorderEditPart) {
				return UnexecutableCommand.INSTANCE;
			}
			if(droppedEditPart instanceof AbstractElementLabelEditPart) {
				return UnexecutableCommand.INSTANCE;
			}
		}
		return super.getDropCommand(request);
	}

	/**
	 * {@inheritedDoc}.
	 */
	@Override
	public Command getDropObjectsCommand(DropObjectsRequest dropRequest) {

		CompoundCommand completeDropCommand = new CompoundCommand("DropObjectsAndArrange"); //$NON-NLS-1$

		// Detect file drop
		if((dropRequest.getObjects().size() > 0) && (dropRequest.getObjects().get(0) instanceof String)) {
			return getDropFileCommand(dropRequest);
		}

		// Create the drop command by composite drop command for each dropped elements
		CompositeCommand gmfDropCommand = new CompositeCommand("DropObjects"); //$NON-NLS-1$
		Iterator<?> iter = dropRequest.getObjects().iterator();
		while(iter.hasNext()) {
			EObject droppedObject = (EObject)iter.next();
			gmfDropCommand.add(getDropObjectCommand(dropRequest, droppedObject));
		}

		// Create the complete drop command by adding an arrange command after drop
		if(!gmfDropCommand.isEmpty()) {

			// Retrieve drop result (most probably created view but not necessarily) and update the request
			CommandResult result = gmfDropCommand.getCommandResult();
			List<?> newValues = (List<?>)result.getReturnValue();
			dropRequest.setResult(newValues);

			// Prepare refresh command
			RefreshConnectionsRequest refreshRequest = new RefreshConnectionsRequest(newValues);
			Command refreshCommand = getHost().getCommand(refreshRequest);

			// Prepare an arrange command to avoid every dropped view to appear at the same location
			ArrangeRequest arrangeRequest = new ArrangeRequest(RequestConstants.REQ_ARRANGE_DEFERRED);
			arrangeRequest.setViewAdaptersToArrange(newValues);
			Command arrangeCommand = getHost().getCommand(arrangeRequest);

			// Update the complete drop command (drop - refresh - arrange)
			Command dropCommand = new ICommandProxy(gmfDropCommand);
			completeDropCommand.add(dropCommand.chain(refreshCommand));
			completeDropCommand.add(arrangeCommand);
		}

		return completeDropCommand;
	}

	protected ICommand getDropObjectCommand(DropObjectsRequest dropRequest, EObject droppedObject) {

		Point location = dropRequest.getLocation().getCopy();

		View dropTargetView = ((IGraphicalEditPart)getHost()).getNotationView();
		EObject dropTargetElement = dropTargetView.getElement();

		String droppedNodeType = registry.getNodeGraphicalType(droppedObject, dropTargetView.getType());
		String droppedEdgeType = registry.getEdgeGraphicalType(droppedObject);

		// Test if a specific drop command should be used
		if(getSpecificDropList().contains(droppedNodeType) || getSpecificDropList().contains(droppedEdgeType)) {
			return getSpecificDropCommand(dropRequest, droppedObject, droppedNodeType, droppedEdgeType);
		}

		// Decide unknown type handling
		if(UNDEFINED_TYPE.equals(droppedNodeType) && UNDEFINED_TYPE.equals(droppedEdgeType)) {
			return getUnknownDropCommand(dropRequest, droppedObject);
		}

		// The dropped element is a node
		if(!UNDEFINED_TYPE.equals(droppedNodeType)) {

			// Drop restriction:
			// - no restriction when dropped on diagram
			// - require containment when dropped on any other EObject		
			 if((dropTargetView instanceof Diagram) || dropTargetElement.eContents().contains(droppedObject)) {
				return getDefaultDropNodeCommand(droppedNodeType, location, droppedObject);
			 }

			 // Allow drop for inherited elements
			 if (dropTargetElement instanceof Classifier && ((Classifier) dropTargetElement).getAllAttributes().contains(droppedObject) ) {
				return getDefaultDropNodeCommand(droppedNodeType, location, droppedObject);
			 }		
			 return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
		}

		// The dropped element is a edge
		if(!UNDEFINED_TYPE.equals(droppedEdgeType)) {

			Collection<?> sources = linkMappingHelper.getSource((Element)droppedObject);
			Collection<?> targets = linkMappingHelper.getTarget((Element)droppedObject);

			// Only manage binary link during drop
			if((sources.size() > 0) && (targets.size() > 0)) {
				EObject source = (EObject)sources.toArray()[0];
				EObject target = (EObject)targets.toArray()[0];
				return getDefaultDropEdgeCommand(droppedObject, source, target, droppedEdgeType, location);
			}

			return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
		}

		return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
	}

	protected ICommand getDefaultDropNodeCommand(String droppedObjectGraphicalType, Point absoluteLocation, EObject droppedObject) {

		IAdaptable elementAdapter = new EObjectAdapter(droppedObject);

		ViewDescriptor descriptor = new ViewDescriptor(elementAdapter, Node.class, droppedObjectGraphicalType, ViewUtil.APPEND, ViewDescriptorUtil.PERSISTED, getDiagramPreferencesHint());
		CreateViewRequest createViewRequest = new CreateViewRequest(descriptor);
		createViewRequest.setLocation(absoluteLocation);

		// Get view creation command for the dropped object
		Command command = getHost().getCommand(createViewRequest);

		// Use the ViewDescriptor as command result, it then can be used as an adaptable to retrieve the View
		return new CommandProxyWithResult(command, descriptor);
	}

	protected ICommand getDefaultDropEdgeCommand(EObject droppedObject, EObject source, EObject target, String droppedEdgeType, Point absoluteLocation) {

		CompositeCommand completeDropCommand = new CompositeCommand("CompleteDropEdge"); //$NON-NLS-1$

		// Find views in current diagram representing source and target 
		Collection<View> sourceViews = getViews(source);
		Collection<View> targetViews = getViews(target);

		IAdaptable sourceViewAdapter = null;
		IAdaptable targetViewAdapter = null;

		// If either a source or target lacks create view for these elements
		// - using defaultDrop command (assumed to be a view creation)
		// - try to create view on host
		if(sourceViews.isEmpty() || targetViews.isEmpty()) {

			CompositeCommand createEndViewsCommand = new CompositeCommand("CreateSourceTargetViews"); //$NON-NLS-1$

			View dropContainerView = ((IGraphicalEditPart)getHost()).getNotationView();
			EObject dropContainerElement = dropContainerView.getElement();

			if(sourceViews.isEmpty()) {
				if(dropContainerElement.eContents().contains(source)) {
					ICommand dropSourceCommand = getDefaultDropNodeCommand(registry.getNodeGraphicalType(source, dropContainerView.getType()), absoluteLocation.getCopy(), source);
					CompositeCommand.compose(createEndViewsCommand, dropSourceCommand);
					sourceViewAdapter = (IAdaptable)dropSourceCommand.getCommandResult().getReturnValue();
				} else {
					return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
				}
			}

			if(targetViews.isEmpty()) {
				if(dropContainerElement.eContents().contains(target)) {
					ICommand dropTargetCommand = getDefaultDropNodeCommand(registry.getNodeGraphicalType(target, dropContainerView.getType()), absoluteLocation.getCopy(), target);
					CompositeCommand.compose(createEndViewsCommand, dropTargetCommand);
					targetViewAdapter = (IAdaptable)dropTargetCommand.getCommandResult().getReturnValue();
				} else {
					return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
				}
			}

			CompositeCommand.compose(completeDropCommand, createEndViewsCommand);
		}

		// Create source adapter
		if(!sourceViews.isEmpty()) { // sourceViewAdapter should still be null in this case
			sourceViewAdapter = new SemanticAdapter(null, sourceViews.toArray()[0]);
		}

		// Create target adapter
		if(!targetViews.isEmpty()) { // targetViewAdapter should still be null in this case
			targetViewAdapter = new SemanticAdapter(null, targetViews.toArray()[0]);
		}

		// Create a view for the dropped link between the source and target view adapters
		IAdaptable droppedObjectAdapter = new SemanticAdapter(droppedObject, null);

		CreateConnectionViewRequest.ConnectionViewDescriptor linkdescriptor = new CreateConnectionViewRequest.ConnectionViewDescriptor(droppedObjectAdapter, droppedEdgeType, getDiagramPreferencesHint());

		CommonDeferredCreateConnectionViewCommand createConnectionViewCommand = new CommonDeferredCreateConnectionViewCommand(getEditingDomain(), droppedEdgeType, sourceViewAdapter, targetViewAdapter, getViewer(), getDiagramPreferencesHint(), linkdescriptor, null);
		createConnectionViewCommand.setElement(droppedObject);

		CompositeCommand.compose(completeDropCommand, createConnectionViewCommand);

		return completeDropCommand.reduce();
	}

	/**
	 * <pre>
	 * Sub-classes have to implement this method in order to provides specific drop command for
	 * element which require a specific treatment.
	 * 
	 * @param dropRequest current drop request
	 * @param droppedEObject the dropped object
	 * @param nodeType the graphical type of the dropped element (node representation)
	 * @param edgeType the graphical type of the dropped element (edge representation)
	 * @return the drop command
	 * </pre>
	 */
	protected ICommand getSpecificDropCommand(DropObjectsRequest dropRequest, EObject droppedEObject, String nodeType, String edgeType) {
		return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
	}

	/**
	 * <pre>
	 * Sub-classes have to implement this method in order to provides drop command for
	 * elements that are not natively known by the diagram.
	 * 
	 * @param dropRequest current drop request
	 * @param droppedEObject the dropped object
	 * @return the drop command
	 * </pre>
	 */
	protected ICommand getUnknownDropCommand(DropObjectsRequest dropRequest, EObject droppedEObject) {
		return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
	}

	protected EditPartViewer getViewer() {
		return ((IGraphicalEditPart)getHost()).getViewer();
	}

	protected TransactionalEditingDomain getEditingDomain() {
		return ((IGraphicalEditPart)getHost()).getEditingDomain();
	}

	protected PreferencesHint getDiagramPreferencesHint() {
		return ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint();
	}

	/**
	 * This methods looks for views representing a given EObject in the host diagram.
	 * 
	 * @param eObject
	 *        the {@link EObject} to look for.
	 * @return the list of {@link View} representing the eObject.
	 */
	private Set<View> getViews(EObject eObject) {
		Set<View> views = new HashSet<View>();

		// Retrieve host diagram
		View hostView = ((IGraphicalEditPart)getHost()).getNotationView();
		View hostDiagram = (hostView instanceof Diagram) ? hostView : hostView.getDiagram();

		// Retrieve all views for the eObject
		Collection<View> relatedViews =  CrossReferencerUtil.getCrossReferencingViews(eObject, hostDiagram.getType());
		
		// Parse and select views from host diagram only	
		Iterator<View> it = relatedViews.iterator();
		while(it.hasNext()) {
			View currentView = it.next();
			if(!(currentView instanceof Diagram) && (currentView.getDiagram() == hostDiagram)) {
				views.add(currentView);
			}
		}

		return views;
	}
}

Back to the top