Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: c5616f7126173d24f2e5c9fe9a000472ace4b5ad (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
/*****************************************************************************
 * Copyright (c) 2010 Atos Origin.
 *
 *
 * 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:
 *  Emilien Perico (Atos Origin) emilien.perico@atosorigin.com - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.uml.diagram.activity.helper;

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

import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.validation.AbstractModelConstraint;
import org.eclipse.emf.validation.EMFEventType;
import org.eclipse.emf.validation.IValidationContext;
import org.eclipse.gmf.runtime.common.core.util.Log;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIPlugin;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIStatusCodes;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.infra.ui.util.EditorUtils;
import org.eclipse.papyrus.uml.diagram.activity.edit.dialogs.ConfirmActivityParameterNodeAndParameterSyncDialog;
import org.eclipse.papyrus.uml.diagram.activity.edit.dialogs.WarningAndLinkDialog;
import org.eclipse.papyrus.uml.diagram.activity.part.CustomMessages;
import org.eclipse.papyrus.uml.diagram.activity.part.UMLDiagramEditorPlugin;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.uml2.common.util.CacheAdapter;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.ActivityParameterNode;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.UMLPackage;

/**
 * ActivityParameterAndParameterSynchronizer is a validator (see corresponding
 * extensions) to synchronize ActivityParameterNode with its Parameter
 *
 */
public class ActivityParameterAndParameterSynchronizer extends AbstractModelConstraint {

	/** The label provider */
	private static final ILabelProvider labelProvider = new AdapterFactoryLabelProvider(UMLDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory());

	/** The activity parameter nodes to remove with parameter */
	private Set<ActivityParameterNode> nodesToRemove = new HashSet<ActivityParameterNode>();

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("restriction")
	@Override
	public IStatus validate(IValidationContext ctx) {
		try {
			EObject eObject = ctx.getTarget();
			if (eObject instanceof Activity) {
				return handleActivityModification((Activity) eObject, ctx);
			} else if (eObject instanceof ActivityParameterNode) {
				return handleActivityParameterNodeModification((ActivityParameterNode) eObject, ctx);
			} else if (eObject instanceof Parameter) {
				return handleParameterModification((Parameter) eObject, ctx);
			}
			return ctx.createSuccessStatus();
		} catch (RuntimeException rte) {
			// avoid throwing uncaught exception which would disable the
			// constraint
			Log.warning(DiagramUIPlugin.getInstance(), DiagramUIStatusCodes.IGNORED_EXCEPTION_WARNING, "Unexpected exception during Activity Parameter Node and Parameter synchronization : ", rte);
			// ensure that the constraint's failure does not prevent
			// modification
			return ctx.createSuccessStatus();
		}
	}

	/**
	 * Handle activity parameter node modification to forbid activity parameter
	 * node type modification
	 *
	 * @param eObject
	 * @param ctx
	 * @return the status
	 */
	private IStatus handleActivityParameterNodeModification(ActivityParameterNode eObject, IValidationContext ctx) {
		if (EMFEventType.SET.equals(ctx.getEventType()) && UMLPackage.eINSTANCE.getTypedElement_Type().equals(ctx.getFeature())) {
			// does not allow type change for activity parameter node, display a
			// message to inform the user
			final Parameter parameter = eObject.getParameter();
			if (parameter != null) {
				final String elementLabel = labelProvider.getText(parameter);
				final String message = NLS.bind(CustomMessages.ActivityParameterAndParameterSynchronizer_UnauthorizedModificationRedirection, elementLabel);
				SafeDialogOpenerDuringValidation<Void> opener = new SafeDialogOpenerDuringValidation<Void>() {

					@Override
					protected Void openDialog() {
						WarningAndLinkDialog dialog = new WarningAndLinkDialog(new Shell(Display.getDefault()), CustomMessages.ActivityParameterAndParameterSynchronizer_UnauthorizedModificationTitle, message, parameter, elementLabel);
						dialog.open();
						return null;
					}
				};
				opener.execute();
				return ctx.createFailureStatus();
			}
		}
		return ctx.createSuccessStatus();
	}

	/**
	 * Handle parameter modification to synchronize the type of the associated
	 * activity parameter nodes
	 *
	 * @param eObject
	 * @param ctx
	 * @return the status
	 */
	private IStatus handleParameterModification(Parameter eObject, IValidationContext ctx) {
		if (EMFEventType.SET.equals(ctx.getEventType()) && UMLPackage.eINSTANCE.getTypedElement_Type().equals(ctx.getFeature())) {
			// change the type of all the associated activity parameter node
			for (ActivityParameterNode node : getActivityParameterNodesFromParameter(eObject)) {
				node.setType(node.getParameter().getType());
			}
		} else if (EMFEventType.SET.equals(ctx.getEventType()) && UMLPackage.eINSTANCE.getNamedElement_Name().equals(ctx.getFeature())) {
			// set the name of all the unnamed associated activity parameter
			// nodes
			for (ActivityParameterNode node : getActivityParameterNodesFromParameter(eObject)) {
				if (node.getName() == null || "".equals(node.getName())) {
					node.setName(eObject.getName());
				}
			}
		}
		return ctx.createSuccessStatus();
	}

	/**
	 * Handle activity modification - add parameter node: set the type with the
	 * associated parameter type - remove parameter: remove all the associated
	 * activity parameter nodes
	 *
	 * @param eObject
	 * @param ctx
	 * @return the status
	 */
	private IStatus handleActivityModification(Activity eObject, IValidationContext ctx) {
		// initialize type when an activity parameter node is created
		if ((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof ActivityParameterNode) {
			ActivityParameterNode activityParameterNode = (ActivityParameterNode) ctx.getFeatureNewValue();
			// The type of an activity parameter node is the same as the type of
			// its parameter.
			activityParameterNode.setType(activityParameterNode.getParameter().getType());
		}
		// constraint: the nodes of an activity must include one
		// ActivityParameterNode for each parameter
		if ((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof Parameter) {
			Parameter parameter = (Parameter) ctx.getFeatureNewValue();
			if (getActivityParameterNodesFromParameter(parameter).isEmpty()) {
				ActivityParameterNode apn = UMLFactory.eINSTANCE.createActivityParameterNode();
				apn.setParameter(parameter);
				apn.setName(parameter.getName());
				apn.setType(parameter.getType());
				Command cmd = getAddActivityParameterNodesCmd(eObject, apn);
				if (cmd.canExecute()) {
					cmd.execute();
				} else {
					return ctx.createFailureStatus();
				}
			}
		}
		// parameter deletion
		else if (((EMFEventType.REMOVE.equals(ctx.getEventType()) && ctx.getFeatureNewValue() instanceof Parameter) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType()))) {
			nodesToRemove.clear();
			// remove associated activity parameter nodes with the parameter
			for (Notification n : ctx.getAllEvents()) {
				if (Notification.SET == n.getEventType() && UMLPackage.eINSTANCE.getActivityParameterNode_Parameter().equals(n.getFeature()) && n.getNotifier() instanceof ActivityParameterNode) {
					nodesToRemove.add((ActivityParameterNode) n.getNotifier());
				}
				// first request is to remove the activity parameter node, it
				// must not be remove because of parameter deletion
				if (Notification.REMOVE == n.getEventType() && n.getOldValue() instanceof ActivityParameterNode) {
					nodesToRemove.remove(n.getOldValue());
				}
			}
			if (!nodesToRemove.isEmpty()) {
				if (askForValidation(nodesToRemove)) {
					Command cmd = getRemoveActivityParameterNodesCmd(eObject, nodesToRemove);
					if (cmd.canExecute()) {
						cmd.execute();
					} else {
						return ctx.createFailureStatus();
					}
				} else {
					return ctx.createFailureStatus();
				}
			}
		}
		return ctx.createSuccessStatus();
	}

	/**
	 * Gets the associated activity parameter nodes from a specified parameter.
	 *
	 * @param parameter
	 *            the parameter
	 * @return the activity parameter nodes
	 */
	private Set<ActivityParameterNode> getActivityParameterNodesFromParameter(Parameter parameter) {
		Set<ActivityParameterNode> references = new HashSet<ActivityParameterNode>();
		if (parameter != null) {
			Collection<Setting> inverseReferences = CacheAdapter.getInstance().getNonNavigableInverseReferences(parameter);
			for (Setting ref : inverseReferences) {
				if (UMLPackage.eINSTANCE.getActivityParameterNode_Parameter().equals(ref.getEStructuralFeature()) && ref.getEObject().eContainer() != null) {
					references.add((ActivityParameterNode) ref.getEObject());
				}
			}
		}
		return references;
	}

	/**
	 * Command to remove the activity parameter nodes that's not have associated
	 * parameter.
	 *
	 * @param owner
	 *            the activity that owns the nodes
	 * @param nodes
	 *            the nodes
	 * @return the remove command
	 */
	private Command getRemoveActivityParameterNodesCmd(Activity owner, Set<ActivityParameterNode> nodes) {
		TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain();
		return RemoveCommand.create(editingdomain, owner, UMLPackage.eINSTANCE.getActivity_Node(), nodes);
	}

	/**
	 * Gets the adds the activity parameter nodes command.
	 *
	 * @param owner
	 *            the owner
	 * @param node
	 *            the node
	 * @return the adds the activity parameter nodes command
	 */
	private Command getAddActivityParameterNodesCmd(Activity owner, ActivityParameterNode node) {
		TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain();
		return AddCommand.create(editingdomain, owner, UMLPackage.eINSTANCE.getActivity_Node(), node);
	}

	/**
	 * Ask the user to validate all the implied modifications (parameters and
	 * activity parameter nodes)
	 *
	 * @param parameterNodes
	 *            the list of impacted activityParameterNodes
	 * @return whether the user validates the modifications
	 */
	protected boolean askForValidation(final Set<? extends NamedElement> parameterNodes) {
		SafeDialogOpenerDuringValidation<Boolean> opener = new SafeDialogOpenerDuringValidation<Boolean>() {

			@Override
			protected Boolean openDialog() {
				return ConfirmActivityParameterNodeAndParameterSyncDialog.openConfirmFromParameter(Display.getDefault().getActiveShell(), parameterNodes, labelProvider);
			}
		};
		return opener.execute();
	}
}

Back to the top