/*****************************************************************************
* Copyright (c) 2015 CEA LIST and others.
*
* 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:
* Ansgar Radermacher (CEA LIST) ansgar.radermacher@cea.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrusrt.umlrt.tooling.ui.widgets;
import java.util.ArrayList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.papyrus.infra.properties.ui.modelelement.ModelElement;
import org.eclipse.papyrus.infra.properties.ui.widgets.ReferenceDialog;
import org.eclipse.papyrus.infra.widgets.providers.DelegatingLabelProvider;
import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider;
import org.eclipse.papyrus.uml.properties.modelelement.UMLModelElement;
import org.eclipse.papyrus.uml.tools.utils.PackageUtil;
import org.eclipse.papyrusrt.umlrt.core.utils.ProtocolUtils;
import org.eclipse.papyrusrt.umlrt.profile.UMLRealTime.RTMessageKind;
import org.eclipse.papyrusrt.umlrt.profile.UMLRealTime.RTMessageSet;
import org.eclipse.papyrusrt.umlrt.tooling.ui.Messages;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.uml2.uml.AnyReceiveEvent;
import org.eclipse.uml2.uml.CallEvent;
import org.eclipse.uml2.uml.Event;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageableElement;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Trigger;
import org.eclipse.uml2.uml.util.UMLUtil;
/**
* Specific event selection dialog for UML/RT. It filters events in function of
* selected port.
*/
public class EventSelectionDialog extends ReferenceDialog {
/**
* Constructor.
*
* @param parent
* @param style
*/
public EventSelectionDialog(Composite parent, int style) {
super(parent, style);
}
protected EList<Package> protocols;
protected EList<Package> conjProtocols;
protected Trigger trigger = null;
protected boolean errorShown = false;
@Override
protected void doBinding() {
editor.setLabelProvider(new EventLabelProvider());
editor.setDirectCreation(input.getDirectCreation(propertyPath));
editor.setMandatory(input.isMandatory(propertyPath));
ModelElement triggerElement = input.getModelElement(propertyPath);
EObject triggerEObj = ((UMLModelElement) triggerElement).getSource();
if (triggerEObj instanceof Trigger) {
trigger = (Trigger) triggerEObj;
} else {
// should not happen
trigger = null;
}
updateProtocols();
editor.setContentProvider(new EventContentProvider());
if (valueEditor != null) {
IObservableValue inputObservableValue = getInputObservableValue();
if (inputObservableValue != null) {
valueEditor.setStrategies();
IValidator modelVal = getValidator();
if (modelVal != null) {
valueEditor.setModelValidator(modelVal);
}
valueEditor.setModelObservable(inputObservableValue);
}
}
}
/**
* Update the information about the used protocols, split into conjugated and "normal" protocols
*/
protected void updateProtocols() {
protocols = new UniqueEList<Package>();
conjProtocols = new UniqueEList<Package>();
if (trigger != null) {
for (Port port : trigger.getPorts()) {
if (port.getType() != null) {
Package protocol = port.getType().getNearestPackage();
if (protocol != null) {
// protocol can be null in case of unresolved proxies (even if type is not null)
if (port.isConjugated()) {
conjProtocols.add(protocol);
} else {
protocols.add(protocol);
}
}
}
}
}
}
/**
* A delegating label provide that displays the icon of the associated
* operation instead of that of the call event itself.
*/
protected class EventLabelProvider extends DelegatingLabelProvider {
public EventLabelProvider() {
super(input.getLabelProvider(propertyPath));
}
/**
* no image in case of any receive event.
*/
@Override
public Image getImage(Object element) {
if (element instanceof AnyReceiveEvent) {
return null;
}
return super.getImage(element);
}
/**
* image of associated operation in case of call event
*/
@Override
protected Image customGetImage(Object element) {
if (element instanceof CallEvent) {
CallEvent ce = (CallEvent) element;
return getImage(ce.getOperation());
}
return null;
}
}
protected class EventContentProvider implements IStaticContentProvider, ITreeContentProvider {
@Override
public Object[] getElements(Object inputElement) {
return getElements();
}
@Override
public void dispose() {
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
updateProtocols();
if (viewer instanceof TreeViewer) {
final TreeViewer treeViewer = (TreeViewer) viewer;
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
treeViewer.expandAll();
}
});
}
}
/**
* Top-level element list
*
* @return
*/
@Override
public Object[] getElements() {
ArrayList<Event> events = new ArrayList<Event>();
// take "any" event from first protocol, if there is one (arbitrary choice)
if (protocols.size() > 0) {
addAnyEvent(events, false, protocols.get(0));
} else if (conjProtocols.size() > 0) {
addAnyEvent(events, true, conjProtocols.get(0));
}
return events.toArray();
}
/**
* Get the specific protocol messages (grouped below the singleAnyReceiveElement as parent)
*/
@Override
public Object[] getChildren(Object parent) {
// get specific messages of protocols
ArrayList<Event> events = new ArrayList<Event>();
EList<Package> allProtocols = new UniqueEList<Package>();
EList<Package> directProtocols = getDirectProtocols();
for (Package protocol : directProtocols) {
allProtocols.addAll(ProtocolUtils.getAllProtocols(protocol));
}
for (Package protocol : allProtocols) {
// only add call events of protocols that are common among the protocols
// referenced by a port
if (ProtocolUtils.isCommonProtocol(directProtocols, protocol)) {
addCallEvents(events, protocol);
}
}
return events.toArray();
}
@Override
public Object getParent(Object parent) {
return null;
}
@Override
public boolean hasChildren(Object event) {
if (event instanceof AnyReceiveEvent) {
return true;
}
return false;
}
}
/**
* add the CallEvents of a protocol to the passed list of events. The function
* takes the conjugation status into account
*
* @param events
* The passed event lsit
* @param protocol
*/
protected void addCallEvents(ArrayList<Event> events, Package protocol) {
// calculate conjugation status. If a protocol is in both lists, only
// inout messages are added
boolean normal = protocols.contains(protocol);
boolean conjugated = conjProtocols.contains(protocol);
for (PackageableElement pe : protocol.getPackagedElements()) {
if (pe instanceof CallEvent) {
CallEvent callEvent = (CallEvent) pe;
Interface intf = callEvent.getOperation().getInterface();
RTMessageSet rtMessageSet = UMLUtil.getStereotypeApplication(intf, RTMessageSet.class);
RTMessageKind kind = rtMessageSet.getRtMsgKind();
if (kind == RTMessageKind.IN_OUT
|| (kind == RTMessageKind.IN && !conjugated)
|| (kind == RTMessageKind.OUT && !normal)) {
events.add(callEvent);
}
}
}
}
/**
* @return protocols that are directly referenced by the ports.
*/
protected EList<Package> getDirectProtocols() {
EList<Package> directProtocols = new UniqueEList<Package>();
directProtocols.addAll(protocols);
directProtocols.addAll(conjProtocols);
return directProtocols;
}
protected static void addAnyEvent(ArrayList<Event> events, boolean conjugated, Package protocolContainer) {
for (PackageableElement pe : protocolContainer.getPackagedElements()) {
if (pe instanceof AnyReceiveEvent) {
events.add((AnyReceiveEvent) pe);
}
}
}
/**
* Obtain the base protocol (UMLRTBaseCommProtocol) from the RTS library
*
* @return the base protocol
*/
public Package getBaseProtocol() {
if (trigger != null) {
Package root = PackageUtil.getRootPackage(trigger);
Package baseProtocol = ProtocolUtils.getBaseProtocol(root);
if (baseProtocol != null) {
return baseProtocol;
}
if (!errorShown) {
errorShown = true;
MessageDialog.openWarning(Display.getCurrent().getActiveShell(),
Messages.EventSelectionDialog_BaseProtocolNotFound_Title,
Messages.EventSelectionDialog_BaseProtocolNotFound_Message);
}
}
return null;
}
}