/**
* Copyright (c) 2012-2016 Marsha Chechik, Alessio Di Sandro, Michalis Famelis,
* Rick Salay.
* 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:
* Alessio Di Sandro - Implementation.
*/
package edu.toronto.cs.se.mmint.mid.diagram.context;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ITextAwareEditPart;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.PlatformUI;
import edu.toronto.cs.se.mmint.MMINT;
import edu.toronto.cs.se.mmint.MMINTConstants;
import edu.toronto.cs.se.mmint.MMINTException;
import edu.toronto.cs.se.mmint.MIDTypeHierarchy;
import edu.toronto.cs.se.mmint.MIDTypeRegistry;
import edu.toronto.cs.se.mmint.mid.MID;
import edu.toronto.cs.se.mmint.mid.Model;
import edu.toronto.cs.se.mmint.mid.diagram.edit.parts.BinaryModelRelEditPart;
import edu.toronto.cs.se.mmint.mid.diagram.edit.parts.MIDEditPart;
import edu.toronto.cs.se.mmint.mid.diagram.edit.parts.Model2EditPart;
import edu.toronto.cs.se.mmint.mid.diagram.edit.parts.ModelEditPart;
import edu.toronto.cs.se.mmint.mid.diagram.edit.parts.ModelRel2EditPart;
import edu.toronto.cs.se.mmint.mid.diagram.edit.parts.ModelRelEditPart;
import edu.toronto.cs.se.mmint.mid.diagram.library.AddModifyConstraintListener;
import edu.toronto.cs.se.mmint.mid.operator.ConversionOperator;
import edu.toronto.cs.se.mmint.mid.operator.Operator;
import edu.toronto.cs.se.mmint.mid.operator.OperatorInput;
import edu.toronto.cs.se.mmint.mid.relationship.ModelRel;
/**
* The handler for the dynamic construction of a context menu for all
* type-related operations (run operator, cast type, validate type).
*
* @author Alessio Di Sandro
*
*/
public class MIDContextMenu extends ContributionItem {
public static final String MMINT_MENU_LABEL = "MMINT";
private static final String MMINT_MENU_OPERATOR_LABEL_INSTANCES = "Run Operator";
private static final String MMINT_MENU_OPERATOR_LABEL_WORKFLOWS = "Add Operator";
private static final String MMINT_MENU_CAST_LABEL = "Cast Type";
private static final String MMINT_MENU_COHERENCE_LABEL = "Check Runtime Coherence";
private static final String MMINT_MENU_ADDCONSTRAINT_LABEL = "Add/Modify Constraint";
private static final String MMINT_MENU_CHECKCONSTRAINT_LABEL = "Check Constraint";
private static final String MMINT_MENU_REFINEBYCONSTRAINT_LABEL = "Refine by Constraint";
private static final String MMINT_MENU_COPY_LABEL = "Copy Model";
private static final String MMINT_MENU_MODELEPEDIA_LABEL = "Wiki";
private static final String MMINT_MENU_MODELEPEDIA_SUBMENU_OPEN_LABEL = "Open Wiki Page";
private static final String MMINT_MENU_MODELEPEDIA_SUBMENU_EDIT_LABEL = "Edit Wiki Page";
private static final String DOWNCAST_LABEL = " (downcast)";
@Override
public boolean isDynamic() {
return true;
}
@Override
public void fill(Menu menu, int index) {
// check selection
ISelection selection = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getSelection();
if (selection == null || selection.isEmpty() || !(selection instanceof StructuredSelection)) {
return;
}
Object[] objects = ((StructuredSelection) selection).toArray();
boolean doAddConstraint = true, doCast = true, doCheckConstraint = true, doCoherence = true, doCopy = true, doModelepedia = true, doOperator = true, doRefineByConstraint = true;
if (objects.length > 1) { // actions that don't work on multiple objects
doAddConstraint = false;
doCast = false;
doCheckConstraint = false;
doCoherence = false;
doCopy = false;
doModelepedia = false;
doRefineByConstraint = false;
}
// get model selection
MID mid = null;
EList<Model> selectedModels = new BasicEList<>();
ITextAwareEditPart editPartLabel = null;
List<GraphicalEditPart> editParts = new ArrayList<>();
for (Object object : objects) {
if (!(
object instanceof MIDEditPart ||
object instanceof ModelEditPart ||
object instanceof Model2EditPart ||
object instanceof ModelRelEditPart ||
object instanceof ModelRel2EditPart ||
object instanceof BinaryModelRelEditPart
)) {
continue;
}
GraphicalEditPart editPart = (GraphicalEditPart) object;
EObject editPartElement = ((View) editPart.getModel()).getElement();
if (editPartElement instanceof MID) {
doAddConstraint = false;
doCast = false;
doCheckConstraint = false;
doCoherence = false;
doCopy = false;
doModelepedia = false;
doRefineByConstraint = false;
if (((MID) editPartElement).isInstancesLevel()) { // instances only
mid = (MID) editPartElement;
}
}
else {
Model model = (Model) editPartElement;
// filter actions based on the MID level
if (model.isTypesLevel() || model.isWorkflowsLevel()) {
doCast = false;
doCheckConstraint = false;
doCoherence = false;
doCopy = false;
doRefineByConstraint = false;
}
if (model.isTypesLevel()) {
doOperator = false;
}
if (model.isWorkflowsLevel()) {
doAddConstraint = false;
}
if (model instanceof ModelRel) { // actions that don't work on model relationships
doCopy = false;
}
if (doAddConstraint || doCast || doCheckConstraint || doCoherence || doCopy || doModelepedia || doOperator || doRefineByConstraint) {
selectedModels.add(model);
}
if (doCast) {
for (Object child : editPart.getChildren()) {
if (child instanceof ITextAwareEditPart) {
editPartLabel = (ITextAwareEditPart) child;
break;
}
}
}
if (doCheckConstraint) {
editParts.add(editPart);
}
}
if (!doOperator && !doCast && !doCheckConstraint && !doCopy && !doAddConstraint && !doModelepedia && !doCoherence && !doRefineByConstraint) { // no action available
return;
}
}
if (mid == null && selectedModels.isEmpty()) { // no relevant edit parts selected
return;
}
// create dynamic menus
MenuItem mmintItem = new MenuItem(menu, SWT.CASCADE, index);
mmintItem.setText(MMINT_MENU_LABEL);
Menu mmintMenu = new Menu(menu);
mmintItem.setMenu(mmintMenu);
MMINT.storeActiveInstanceMIDFile();
// operator
if (doOperator) {
if (mid == null) {
mid = selectedModels.get(0).getMIDContainer();
}
MIDTypeHierarchy.clearCachedRuntimeTypes();
String polyPreference = null;
if (mid.isWorkflowsLevel()) { // disable polymorphism at the workflow level
polyPreference = MMINT.getPreference(MMINTConstants.PREFERENCE_MENU_POLYMORPHISM_ENABLED);
MMINT.setPreference(MMINTConstants.PREFERENCE_MENU_POLYMORPHISM_ENABLED, "false");
}
List<Operator> executableOperators = new ArrayList<>();
List<EList<OperatorInput>> executableOperatorsInputs = new ArrayList<>();
for (Operator operatorType : MIDTypeRegistry.getOperatorTypes()) {
try {
EList<OperatorInput> executableOperatorInputs = operatorType.checkAllowedInputs(selectedModels);
if (executableOperatorInputs == null) {
continue;
}
executableOperators.add(operatorType);
executableOperatorsInputs.add(executableOperatorInputs);
}
catch (MMINTException e) {
continue;
}
}
if (polyPreference != null) { // restore polymorphism preference
MMINT.setPreference(MMINTConstants.PREFERENCE_MENU_POLYMORPHISM_ENABLED, polyPreference);
}
MIDTypeHierarchy.clearCachedRuntimeTypes();
if (!executableOperators.isEmpty()) {
MenuItem operatorItem = new MenuItem(mmintMenu, SWT.CASCADE);
String menuLabel = (mid.isInstancesLevel()) ?
MMINT_MENU_OPERATOR_LABEL_INSTANCES :
MMINT_MENU_OPERATOR_LABEL_WORKFLOWS;
operatorItem.setText(menuLabel);
Menu operatorMenu = new Menu(mmintMenu);
operatorItem.setMenu(operatorMenu);
for (int i = 0; i < executableOperators.size(); i++) {
Operator executableOperator = executableOperators.get(i);
//TODO MMINT[OPERATOR] There should be a visual match between formal and actual parameter, with indication of coercion
String text = executableOperator.toString();
MenuItem operatorSubitem = new MenuItem(operatorMenu, SWT.NONE);
operatorSubitem.setText(text);
operatorSubitem.addSelectionListener(
new MIDContextRunOperatorListener(
menuLabel,
executableOperator,
executableOperatorsInputs.get(i),
mid
)
);
}
}
}
// cast
if (doCast) {
// polymorphism
MIDTypeHierarchy.clearCachedRuntimeTypes();
EList<Model> runtimeModelTypes = new BasicEList<>();
try {
runtimeModelTypes.addAll(selectedModels.get(0).getRuntimeTypes());
}
catch (MMINTException e) {
// do nothing
}
MIDTypeHierarchy.clearCachedRuntimeTypes();
if (runtimeModelTypes.size() > 1) {
MenuItem castItem = new MenuItem(mmintMenu, SWT.CASCADE);
castItem.setText(MMINT_MENU_CAST_LABEL);
Menu castMenu = new Menu(mmintMenu);
castItem.setMenu(castMenu);
boolean isDowncast = false;
for (Model runtimeModelType : runtimeModelTypes) {
if (runtimeModelType.getUri().equals(selectedModels.get(0).getMetatypeUri())) {
isDowncast = true;
continue;
}
MenuItem castSubitem = new MenuItem(castMenu, SWT.NONE);
String text = (isDowncast) ? runtimeModelType.getName() + DOWNCAST_LABEL : runtimeModelType.getName();
castSubitem.setText(text);
castSubitem.addSelectionListener(
new MIDContextCastTypeListener(MMINT_MENU_CAST_LABEL, selectedModels.get(0), runtimeModelType, editPartLabel)
);
}
}
}
// coherence
if (doCoherence) {
Map<Model, Set<List<ConversionOperator>>> multiplePathConversions = MIDTypeHierarchy.getMultiplePathConversions(selectedModels.get(0).getMetatypeUri());
if (!multiplePathConversions.isEmpty()) {
MenuItem coherenceItem = new MenuItem(mmintMenu, SWT.CASCADE);
coherenceItem.setText(MMINT_MENU_COHERENCE_LABEL);
Menu coherenceMenu = new Menu(mmintMenu);
coherenceItem.setMenu(coherenceMenu);
for (Map.Entry<Model, Set<List<ConversionOperator>>> conversionPathsEntry : multiplePathConversions.entrySet()) {
MenuItem coherenceSubitem = new MenuItem(coherenceMenu, SWT.NONE);
coherenceSubitem.setText("To " + conversionPathsEntry.getKey().getName());
coherenceSubitem.addSelectionListener(
new MIDContextCheckCoherenceListener(MMINT_MENU_COHERENCE_LABEL, selectedModels.get(0), conversionPathsEntry.getValue())
);
}
}
}
// add constraint
if (doAddConstraint) {
MenuItem constraintItem = new MenuItem(mmintMenu, SWT.NONE);
constraintItem.setText(MMINT_MENU_ADDCONSTRAINT_LABEL);
constraintItem.addSelectionListener(
new AddModifyConstraintListener(MMINT_MENU_ADDCONSTRAINT_LABEL, selectedModels.get(0))
);
}
// check constraint
if (doCheckConstraint) {
MenuItem constraintItem = new MenuItem(mmintMenu, SWT.NONE);
constraintItem.setText(MMINT_MENU_CHECKCONSTRAINT_LABEL);
constraintItem.addSelectionListener(
new MIDContextCheckConstraintListener(MMINT_MENU_CHECKCONSTRAINT_LABEL, selectedModels.get(0), editParts.get(0))
);
}
// refine
if (doRefineByConstraint) {
MenuItem refineItem = new MenuItem(mmintMenu, SWT.NONE);
refineItem.setText(MMINT_MENU_REFINEBYCONSTRAINT_LABEL);
refineItem.addSelectionListener(
new MIDContextRefineByConstraintListener(MMINT_MENU_REFINEBYCONSTRAINT_LABEL, selectedModels.get(0))
);
}
// copy
if (doCopy) {
MenuItem copyItem = new MenuItem(mmintMenu, SWT.NONE);
copyItem.setText(MMINT_MENU_COPY_LABEL);
copyItem.addSelectionListener(
new MIDContextCopyModelListener(MMINT_MENU_COPY_LABEL, selectedModels.get(0))
);
}
// modelepedia
if (doModelepedia) {
Model model = selectedModels.get(0);
if (model.isInstancesLevel()) {
model = model.getMetatype();
}
MenuItem modelepediaItem = new MenuItem(mmintMenu, SWT.CASCADE);
modelepediaItem.setText(MMINT_MENU_MODELEPEDIA_LABEL);
Menu modelepediaMenu = new Menu(mmintMenu);
modelepediaItem.setMenu(modelepediaMenu);
MenuItem openModelepediaItem = new MenuItem(modelepediaMenu, SWT.NONE);
openModelepediaItem.setText(MMINT_MENU_MODELEPEDIA_SUBMENU_OPEN_LABEL);
openModelepediaItem.addSelectionListener(
new MIDContextOpenModelepediaListener(model)
);
MenuItem editModelepediaItem = new MenuItem(modelepediaMenu, SWT.NONE);
editModelepediaItem.setText(MMINT_MENU_MODELEPEDIA_SUBMENU_EDIT_LABEL);
editModelepediaItem.addSelectionListener(
new MIDContextEditModelepediaListener(model)
);
}
}
}