/*****************************************************************************
* 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.editor.PapyrusMultiDiagramEditor;
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>>() {
public Iterable<ActivityEdge> apply(EObject from) {
if(from instanceof ActivityNode) {
ActivityNode activityNode = (ActivityNode)from;
Iterable<ActivityEdge> incomingInterruptibleEdge = Iterables.filter(activityNode.getIncomings(), new Predicate<EObject>() {
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>() {
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 PapyrusMultiDiagramEditor) {
PapyrusMultiDiagramEditor papyrusEditor = (PapyrusMultiDiagramEditor)editor;
return papyrusEditor.getDiagramEditPart();
}
return null;
}
}