/*****************************************************************************
* 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.Collection;
import java.util.Collections;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.NotificationFilter;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.UnexecutableCommand;
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.services.ViewService;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.editor.PapyrusMultiDiagramEditor;
import org.eclipse.papyrus.uml.diagram.activity.edit.part.interfaces.InterruptibleEdge;
import org.eclipse.papyrus.uml.diagram.activity.edit.parts.ControlFlowInterruptibleIconEditPart;
import org.eclipse.papyrus.uml.diagram.activity.edit.parts.ObjectFlowInterruptibleIconEditPart;
import org.eclipse.papyrus.uml.diagram.activity.part.UMLDiagramEditorPlugin;
import org.eclipse.papyrus.uml.diagram.activity.request.InterruptibleEdgeRequest;
import org.eclipse.papyrus.uml.diagram.common.listeners.AbstractPapyrusModifcationTriggerListener;
import org.eclipse.papyrus.uml.diagram.common.util.DiagramEditPartsUtil;
import org.eclipse.papyrus.uml.diagram.common.util.functions.EObjectToViewFunction;
import org.eclipse.papyrus.uml.diagram.common.util.functions.SettingToEObjectFunction;
import org.eclipse.papyrus.uml.diagram.common.util.predicates.ReferencingViewPredicate;
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.UMLPackage;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.Iterables;
/**
* This listener handle Interruptible Edge
*
* @author arthur daussy
*
*/
public class InterruptibleEdgeListener extends AbstractPapyrusModifcationTriggerListener {
/**
* Id of all Visual ID of the interruptible Icon
*/
private static ImmutableBiMap<EClass, String> INTERRUPTIBLE_EDGE_ICON_VISUAL_ID_COLLECTION = ImmutableBiMap.of(UMLPackage.Literals.OBJECT_FLOW, String.valueOf(ObjectFlowInterruptibleIconEditPart.VISUAL_ID), UMLPackage.Literals.CONTROL_FLOW, String.valueOf(ControlFlowInterruptibleIconEditPart.VISUAL_ID));
private static NotificationFilter FEATURE_FILTER = null;
public static NotificationFilter getFEATURE_FILTER() {
if(FEATURE_FILTER == null) {
FEATURE_FILTER = NotificationFilter.createFeatureFilter(UMLPackage.Literals.ACTIVITY_EDGE__INTERRUPTS);
}
return FEATURE_FILTER;
}
/**
* 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;
}
/**
* This command will react on a SET event of the structural feature describe in
* {@link InterruptibleEdgeListener#isCorrectStructuralfeature(EStructuralFeature)} This will create a new view if the newValue != null or delete
* it if null
*/
@Override
protected ICommand getModificationCommand(Notification notif) {
if(Notification.SET == notif.getEventType()) {
CompositeCommand cc = new CompositeCommand("Interruptible Edge Command");////$NON-NLS-0$
//Handling views
final Iterable<IGraphicalEditPart> edgesEditPart = DiagramEditPartsUtil.getChildrenByEObject((EObject)notif.getNotifier(), getDiagramEditPart(), true);
InterruptibleEdgeRequest request = new InterruptibleEdgeRequest();
Iterable<View> views = getReferencingView(notif);
if(notif.getNewValue() != null) {
//handle create view
request.setType(InterruptibleEdgeRequest.SET_INTERRUPTIBLE_EDGE);
for(View view : views) {
try {
String visualID = INTERRUPTIBLE_EDGE_ICON_VISUAL_ID_COLLECTION.get(view.getElement().eClass());
ICommand createViewCommand = createInterruptibleEdgeIcon(view, visualID);
if(createViewCommand != null && createViewCommand.canExecute()) {
cc.compose(createViewCommand);
}
} catch (NullPointerException e) {
throw new RuntimeException("Unable to find the Visual ID of the Icon of the interruptible Edge for element" + view.getElement());
}
}
} else {
//handle delete view
request.setType(InterruptibleEdgeRequest.UNSET_INTERRUPTIBLE_EDGE);
for(View view : views) {
try {
String visualID = INTERRUPTIBLE_EDGE_ICON_VISUAL_ID_COLLECTION.get(view.getElement().eClass());
ICommand destroyCommand = destroyInterruptibleIcon((View)view, visualID);
if(destroyCommand != null && destroyCommand.canExecute()) {
cc.compose(destroyCommand);
}
} catch (NullPointerException e) {
throw new RuntimeException("Unable to find the Visual ID of the Icon of the interruptible Edge for element" + view.getElement());
}
}
}
for(IGraphicalEditPart edgeEditPart : edgesEditPart) {
if(edgeEditPart != null && edgeEditPart instanceof InterruptibleEdge && edgeEditPart.getModel() instanceof View) {
//Ask for the edit if something more else has to be done
Command command = edgeEditPart.getCommand(request);
if(command != null && command.canExecute()) {
cc.compose(new CommandProxy(command));
}
}
}
return cc;
}
return null;
}
/**
* Get all the view from Notation Model which represent the notifier.
* Will only return the view of the same type than the notifier view
*
* @param notif
* @param edgeEditPart
* @return
*/
public Iterable<View> getReferencingView(Notification notif) {
final ActivityEdge element = getElement(notif);
Resource eResource = element.eResource();
if(eResource != null) {
ECrossReferenceAdapter adapter = ECrossReferenceAdapter.getCrossReferenceAdapter(eResource.getResourceSet());
if(adapter == null) {
adapter = new ECrossReferenceAdapter();
}
Collection<Setting> inverseReferences = adapter.getInverseReferences(element);
Iterable<EObject> settings = Iterables.transform(inverseReferences, new SettingToEObjectFunction());
Iterable<EObject> eObjects = Iterables.filter(settings, new ReferencingViewPredicate(element));
Iterable<View> views = Iterables.transform(eObjects, new EObjectToViewFunction());
return views;
}
return Collections.emptyList();
}
private TransactionalEditingDomain getEditingDomain(View model) {
DiagramEditPart diagramEditPart = getDiagramEditPart();
if(diagramEditPart != null) {
return diagramEditPart.getEditingDomain();
}
EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(model);
if(editingDomain instanceof TransactionalEditingDomain) {
return (TransactionalEditingDomain)editingDomain;
}
return null;
}
/**
* Create the command to withdraw interruptible edge icon from the activity edge
*
* @return
*/
private ICommand destroyInterruptibleIcon(final View model, final String visualID) {
TransactionalEditingDomain editingDomain = getEditingDomain(model);
if(editingDomain != null) {
AbstractTransactionalCommand cmd = new AbstractTransactionalCommand(editingDomain, "Destroy Interruptible Edge Icon", null) {
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
if(model != null) {
View interruptibleEdgeIconView = ViewUtil.getChildBySemanticHint(model, visualID);
ViewUtil.destroy(interruptibleEdgeIconView);
return CommandResult.newOKCommandResult();
}
return null;
}
};
return cmd;
}
return null;
}
/**
* Create the Interruptible Edge Icon View
*
* @return Command to be executed or {@link UnexecutableCommand#INSTANCE} if unable to create
*/
private ICommand createInterruptibleEdgeIcon(final View model, final String visualID) {
TransactionalEditingDomain editingDomain = getEditingDomain(model);
if(editingDomain != null) {
AbstractTransactionalCommand cmd = new AbstractTransactionalCommand(editingDomain, "Create Interruptible Edge Icon", null) {
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
if(model != null) {
Node node = ViewService.createNode((View)model, visualID, UMLDiagramEditorPlugin.DIAGRAM_PREFERENCES_HINT);
if(node != null) {
return CommandResult.newOKCommandResult(node);
} else {
return CommandResult.newErrorCommandResult("Unable to create the view for Interruptible Edge label");
}
}
return null;
}
/*
* TODO test if needed
* (non-Javadoc)
*
* @see
* org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand#doUndo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
protected IStatus doUndo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
IStatus status = super.doUndo(monitor, info);
getDiagramEditPart().refresh();
return status;
}
/*
* TODO test if needed
* (non-Javadoc)
*
* @see
* org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand#doUndo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
protected IStatus doRedo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
IStatus status = super.doRedo(monitor, info);
getDiagramEditPart().refresh();
return status;
}
};
return cmd;
}
return null;
}
protected ActivityEdge getElement(Notification notif) {
Object element = notif.getNotifier();
if(element instanceof ActivityEdge) {
return (ActivityEdge)element;
}
return null;
}
@Override
public NotificationFilter getFilter() {
return getFEATURE_FILTER();
}
}