Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: a614584110f341bc5b14205da023f2dc7993cfbf (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
/*****************************************************************************
 * Copyright (c) 2011 Atos.
 *
 *
 * 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:
 *   Atos - Initial API and implementation
 *   Bug 366159 - [ActivityDiagram] Activity Diagram should be able to handle correctly Interruptible Edge
 *
 *****************************************************************************/
package org.eclipse.papyrus.uml.diagram.activity.listeners;

import java.util.Collections;
import java.util.Iterator;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.transaction.NotificationFilter;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.papyrus.commands.wrappers.EMFtoGMFCommandWrapper;
import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
import org.eclipse.papyrus.infra.widgets.toolbox.notification.Type;
import org.eclipse.papyrus.infra.widgets.toolbox.notification.builders.NotificationBuilder;
import org.eclipse.papyrus.uml.diagram.activity.helper.UMLValidationHelper;
import org.eclipse.papyrus.uml.diagram.common.listeners.AbstractPapyrusModifcationTriggerListener;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.uml2.uml.ActivityEdge;
import org.eclipse.uml2.uml.ActivityNode;
import org.eclipse.uml2.uml.UMLPackage;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;

/**
 * Listen the {@link UMLPackage.Literals#ACTIVITY_NODE__IN_INTERRUPTIBLE_REGION} feature in orfer to prevetn it to violate the constaint
 * "Interrupting edges of a region must have their source node in the region and their target node outside the region in the same activity containing the region."
 *
 * @author adaussy
 *
 */
public class InInterruptibleActivityRegionListener extends AbstractPapyrusModifcationTriggerListener {

	private static NotificationFilter FEATURE_FILTER = null;

	public static NotificationFilter getFEATURE_FILTER() {
		if (FEATURE_FILTER == null) {
			FEATURE_FILTER = NotificationFilter.createFeatureFilter(UMLPackage.Literals.ACTIVITY_NODE__IN_INTERRUPTIBLE_REGION);
		}
		return FEATURE_FILTER;
	}

	protected ActivityNode getElement(Notification notif) {
		try {
			return (ActivityNode) notif.getNotifier();
		} catch (ClassCastException e) {
			throw new RuntimeException("InInterruptibleActivityRegionListener should only be notified by ActivityNode");
		}
	}

	/**
	 * Get the list of all starting or ending Interruptible Edge wich are related to this {@link ActivityNode} and its descendant.
	 * Those Iterable can be filled with null elements so test each element for null
	 *
	 * @param node
	 * @return
	 */
	public Iterator<Iterable<ActivityEdge>> getActivityEdgeImpactedWithThisChange(ActivityNode node) {
		Iterator<Iterable<ActivityEdge>> activityEdges = Iterators.transform(Iterators.concat(Collections.singleton(node).iterator(), node.eAllContents()), new Function<EObject, Iterable<ActivityEdge>>() {

			@Override
			public Iterable<ActivityEdge> apply(EObject from) {
				if (from instanceof ActivityNode) {
					ActivityNode activityNode = (ActivityNode) from;
					Iterable<ActivityEdge> incomingInterruptibleEdge = Iterables.filter(activityNode.getIncomings(), new Predicate<EObject>() {

						@Override
						public boolean apply(EObject input) {
							if (input instanceof ActivityEdge) {
								return ((ActivityEdge) input).getInterrupts() != null;
							}
							return false;
						}
					});
					Iterable<ActivityEdge> outcomingEdgeInterruptibleEdge = Iterables.filter(activityNode.getOutgoings(), new Predicate<EObject>() {

						@Override
						public boolean apply(EObject input) {
							if (input instanceof ActivityEdge) {
								return ((ActivityEdge) input).getInterrupts() != null;
							}
							return false;
						}
					});
					Iterable<ActivityEdge> allInterruptibleEdge = Iterables.concat(outcomingEdgeInterruptibleEdge, incomingInterruptibleEdge);
					if (!Iterables.isEmpty(allInterruptibleEdge)) {
						return allInterruptibleEdge;
					}
				}
				return null;
			}
		});
		return activityEdges;
	}

	@Override
	public NotificationFilter getFilter() {
		return getFEATURE_FILTER();
	}

	@Override
	protected ICommand getModificationCommand(Notification notif) {
		ActivityNode node = getElement(notif);
		// Get the the interruptible Edge Starting or Going from this node or its descendant
		Iterator<Iterable<ActivityEdge>> activityEdges = getActivityEdgeImpactedWithThisChange(node);
		while (activityEdges.hasNext()) {
			Iterable<ActivityEdge> interruptibleEdge = activityEdges.next();
			if (interruptibleEdge != null) {
				for (ActivityEdge interrpEdge : interruptibleEdge) {
					if (!UMLValidationHelper.validateInterruptibleEdge(interrpEdge, interrpEdge.getInterrupts())) {
						NotificationBuilder popup = new NotificationBuilder().setAsynchronous(true).setTemporary(true).setMessage("The Activity Edge " + interrpEdge.getName() + " can not interrupt its referencing region because it violates a constraint")
								.setType(Type.INFO);
						popup.run();
						return new EMFtoGMFCommandWrapper(new SetCommand(getDiagramEditPart().getEditingDomain(), interrpEdge, UMLPackage.Literals.ACTIVITY_EDGE__INTERRUPTS, null));
					}
				}
			}
		}
		return null;
	}

	/**
	 * get the edit part registry
	 *
	 * @return
	 */
	protected DiagramEditPart getDiagramEditPart() {
		IWorkbench wb = PlatformUI.getWorkbench();
		IWorkbenchPage page = wb.getActiveWorkbenchWindow().getActivePage();
		IEditorPart editor = page.getActiveEditor();
		if (editor instanceof IMultiDiagramEditor) {
			IMultiDiagramEditor papyrusEditor = (IMultiDiagramEditor) editor;
			return (DiagramEditPart) papyrusEditor.getAdapter(DiagramEditPart.class);
		}
		return null;
	}
}

Back to the top