/***************************************************************************** * Copyright (c) 2010 Atos Origin. * * * 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 Origin - Initial API and implementation * Olivier Melois (Atos) : olivier.melois@atos.net - 371712 * Arthur Daussy (Atos) : arthur.daussy@atos.net - 371712 * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.activity.helper; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.CompoundCommand; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; 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.EcoreUtil; import org.eclipse.emf.ecore.xmi.XMIResource; import org.eclipse.emf.edit.command.AddCommand; import org.eclipse.emf.edit.command.CopyCommand; import org.eclipse.emf.edit.command.RemoveCommand; import org.eclipse.emf.edit.command.SetCommand; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.validation.AbstractModelConstraint; import org.eclipse.emf.validation.EMFEventType; import org.eclipse.emf.validation.IValidationContext; import org.eclipse.emf.workspace.WorkspaceEditingDomainFactory; import org.eclipse.gmf.runtime.common.core.util.Log; import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIPlugin; import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIStatusCodes; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.papyrus.infra.core.utils.EditorUtils; import org.eclipse.papyrus.uml.diagram.activity.commands.CreatePinToParameterLinkEAnnotation; import org.eclipse.papyrus.uml.diagram.activity.edit.dialogs.ConfirmPinAndParameterSyncDialog; import org.eclipse.papyrus.uml.diagram.activity.edit.dialogs.WarningAndCreateAttributeDialog; import org.eclipse.papyrus.uml.diagram.activity.edit.dialogs.WarningAndCreateParameterDialog; import org.eclipse.papyrus.uml.diagram.activity.edit.dialogs.WarningAndLinkDialog; import org.eclipse.papyrus.uml.diagram.activity.edit.parts.BroadcastSignalActionEditPart; import org.eclipse.papyrus.uml.diagram.activity.handlers.SynchronizePinsParametersHandler; import org.eclipse.papyrus.uml.diagram.activity.helper.datastructure.LinkPinToParameter; import org.eclipse.papyrus.uml.diagram.activity.part.Messages; import org.eclipse.papyrus.uml.diagram.activity.part.UMLDiagramEditorPlugin; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.uml2.common.util.CacheAdapter; import org.eclipse.uml2.uml.Action; import org.eclipse.uml2.uml.ActivityNode; import org.eclipse.uml2.uml.AddStructuralFeatureValueAction; import org.eclipse.uml2.uml.AddVariableValueAction; import org.eclipse.uml2.uml.Behavior; import org.eclipse.uml2.uml.BroadcastSignalAction; import org.eclipse.uml2.uml.CallAction; import org.eclipse.uml2.uml.CallBehaviorAction; import org.eclipse.uml2.uml.CallOperationAction; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.CreateObjectAction; import org.eclipse.uml2.uml.DestroyObjectAction; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.InputPin; import org.eclipse.uml2.uml.InvocationAction; import org.eclipse.uml2.uml.LiteralInteger; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.ObjectNode; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.OutputPin; import org.eclipse.uml2.uml.Parameter; import org.eclipse.uml2.uml.ParameterDirectionKind; import org.eclipse.uml2.uml.Pin; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.ReadStructuralFeatureAction; import org.eclipse.uml2.uml.ReadVariableAction; import org.eclipse.uml2.uml.SendObjectAction; import org.eclipse.uml2.uml.SendSignalAction; import org.eclipse.uml2.uml.Signal; import org.eclipse.uml2.uml.StructuralFeature; import org.eclipse.uml2.uml.StructuralFeatureAction; import org.eclipse.uml2.uml.Type; import org.eclipse.uml2.uml.TypedElement; import org.eclipse.uml2.uml.UMLFactory; import org.eclipse.uml2.uml.UMLPackage; import org.eclipse.uml2.uml.ValueSpecification; import org.eclipse.uml2.uml.Variable; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; /** * The PinAndParameterSynchronizer is a validator (see corresponding extensions) * which ensure Pins and their corresponding (if a correspondance can be * established) Parameters are correctly synchronized * */ public class PinAndParameterSynchronizer extends AbstractModelConstraint { private static final String RESULT_IN_READ_VARIABLE_ACTION = "result"; private static final String VALUE_IN_ADD_VARIABLE_VALUE_ACTION = "value"; private static final String INSERT_AT_IN_ADD_VARIABLE_VALUE_ACTION = "insertAt"; private static final String TARGET_IN_DESTROY_OBJECT_ACTION = "target"; private static final String VALUE_PIN_IN_STRUCTURAL_FEATURE_VALUE_ACTION = VALUE_IN_ADD_VARIABLE_VALUE_ACTION; private static final String RESULT_PIN_READ_SRTUCTURAL_ACTION = "result"; private static final String OBJECT_PIN_IN_READS_STRUCTURAL_ACTION = "object"; /** The label provider */ private static final ILabelProvider labelProvider = new AdapterFactoryLabelProvider(UMLDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory()); /** The constant to initialize target pin name */ private static final String TARGET_PIN_INITIALIZATION_NAME = "target"; /** The constant to initialize request pin name */ private static final String REQUEST_PIN_INITIALIZATION_NAME = "request"; /** The constant to initialize result pin name */ private static final String RESULT_PIN_INITIALIZATION_NAME = "result"; /** * Validate modification and update associated elements if necessary * * @see org.eclipse.emf.validation.AbstractModelConstraint#validate(org.eclipse.emf.validation.IValidationContext) * * @param ctx * validation context * @return status */ @Override public IStatus validate(IValidationContext ctx) { try { EObject eObject = ctx.getTarget(); // handle action creation separately not to confuse with case when // Behavior is modified /********* * Done *********/ if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof CallBehaviorAction) { // CallBehaviorAction created CompoundCommand cmd = getResetPinsCmd((CallAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof CallOperationAction) { // CallOperationAction created CompoundCommand cmd = getResetPinsCmd((CallAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof SendSignalAction) { // SendSignalAction created CompoundCommand cmd = getResetPinsCmd((SendSignalAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof SendObjectAction) { // SendObjectAction created CompoundCommand cmd = getResetPinsCmd((SendObjectAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof DestroyObjectAction) { // SendObjectAction created CompoundCommand cmd = getResetPinsCmd((DestroyObjectAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && (ctx.getFeatureNewValue() instanceof AddStructuralFeatureValueAction)) { // CreateObject Action created CompoundCommand cmd = getResetPinsCmd((AddStructuralFeatureValueAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof ReadStructuralFeatureAction) { // SendObjectAction created CompoundCommand cmd = getResetPinsCmd((ReadStructuralFeatureAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof AddVariableValueAction) { // SendObjectAction created CompoundCommand cmd = getResetPinsCmd((AddVariableValueAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof ReadVariableAction) { // SendObjectAction created CompoundCommand cmd = getResetPinsCmd((ReadVariableAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof BroadcastSignalAction) { // SendObjectAction created CompoundCommand cmd = getResetPinsCmd((InvocationAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } /********* * Done *********/ } else if((EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) && ctx.getFeatureNewValue() instanceof CreateObjectAction) { // CreateObject Action created CompoundCommand cmd = getResetPinsCmd((CreateObjectAction)ctx.getFeatureNewValue()); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } } /********* * Done *********/ // handle Operation move for redefining target pin type if((EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) && UMLPackage.eINSTANCE.getOperation_Class().equals(ctx.getFeature()) && eObject instanceof Operation) { // Operation moved CompoundCommand cmd = getUpdateTargetPinsType((Operation)eObject); if(!cmd.isEmpty() && cmd.canExecute()) { if(askForValidation(getCallingActions((Operation)eObject))) { cmd.execute(); } else { return ctx.createFailureStatus(); } } } /********* * Done *********/ if(eObject instanceof ValueSpecification) { // the value specification may be in an upperValue or lowerValue // replace values with appropriate ones EObject topValueSpec = eObject; while(topValueSpec.eContainer() instanceof ValueSpecification) { topValueSpec = topValueSpec.eContainer(); } eObject = topValueSpec.eContainer(); } /********* * Done *********/ if(eObject instanceof Pin) { // special case for CallOperationAction target pin : no // synchronization, but type Element owner = ((Pin)eObject).getOwner(); Pin target = null; if(owner instanceof CallOperationAction) { target = ((CallOperationAction)owner).getTarget(); } if(target != null && target.equals(eObject)) { return handleTargetPinModification((Pin)eObject, ctx); } else { // Pin is modified, report modification on // Parameter/Property return handlePinModification((Pin)eObject, ctx); } /********* * Done *********/ } else if(eObject instanceof Parameter) { // Parameter is modified, report modification on Pins return handleParameterModification((Parameter)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof Property) { // Property is modified, report modification on Pins return handlePropertyModification((Property)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof CallOperationAction) { // action is modified, ensure deleted/added Pin impact a // Parameter return handleCallOperationActionModification((CallOperationAction)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof Operation) { // Operation is modified, ensure deleted/added Parameter impact // Pins return handleOperationModification((Operation)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof CallBehaviorAction) { // action is modified, ensure deleted/added Pin impact a // Parameter return handleCallBehaviorActionModification((CallBehaviorAction)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof Behavior) { // Behavior is modified, ensure deleted/added Parameter impact // Pins return handleBehaviorModification((Behavior)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof SendSignalAction) { // action is modified, ensure deleted/added Pin impact a // Property return handleSendSignalActionModification((SendSignalAction)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof Signal) { // Signal is modified, ensure deleted/added Property impact Pins return handleSignalModification((Signal)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof SendObjectAction) { // action is modified, ensure deleted/added Pin are authorized return handleSendObjectActionModification((SendObjectAction)eObject, ctx); /********* * Done *********/ } else if(eObject instanceof BroadcastSignalAction) { // action is modified, ensure deleted/added Pin impact a // Property return handleBroadcastSignalActionModification((BroadcastSignalAction)eObject, ctx); } return ctx.createSuccessStatus(); } catch (RuntimeException rte) { // avoid throwing uncaught exception which would disable the // constraint Log.warning(DiagramUIPlugin.getInstance(), DiagramUIStatusCodes.IGNORED_EXCEPTION_WARNING, "Unexpected exception during Pin and Parameter synchronization : ", rte); // ensure that the constraint's failure does not prevent // modification return ctx.createSuccessStatus(); } } /** * Test if the Pin feature impacts the parameter or if the Parameter feature * impacts pins * * @param modifiedFeature * the feature to test * @return true if the feature impacts the associated Parameter or Pin * objects */ protected boolean testPinOrParameterOrPropertyFeature(EStructuralFeature modifiedFeature) { boolean type = UMLPackage.eINSTANCE.getTypedElement_Type().equals(modifiedFeature); boolean ordering = UMLPackage.eINSTANCE.getMultiplicityElement_IsOrdered().equals(modifiedFeature); boolean multiplicity = UMLPackage.eINSTANCE.getMultiplicityElement_IsUnique().equals(modifiedFeature) || UMLPackage.eINSTANCE.getMultiplicityElement_Lower().equals(modifiedFeature) || UMLPackage.eINSTANCE.getMultiplicityElement_LowerValue().equals(modifiedFeature) || UMLPackage.eINSTANCE.getMultiplicityElement_Upper().equals(modifiedFeature) || UMLPackage.eINSTANCE.getMultiplicityElement_UpperValue().equals(modifiedFeature); boolean inAValueSpecification = ValueSpecification.class.isAssignableFrom(modifiedFeature.getContainerClass()); return type || ordering || multiplicity || inAValueSpecification; } /** * Test if the Action feature impacts the number of Pins and Parameters * * @param modifiedFeature * the feature to test * @return true if the feature impacts the number of Parameters or Pins */ protected boolean testActionFeature(EStructuralFeature modifiedFeature) { boolean input = UMLPackage.eINSTANCE.getInvocationAction_Argument().equals(modifiedFeature) || UMLPackage.eINSTANCE.getCallOperationAction_Target().equals(modifiedFeature); boolean output = UMLPackage.eINSTANCE.getCallAction_Result().equals(modifiedFeature); return input || output; } /** * Ensure target Pin modification is correct * * @param pin * modified pin * @param ctx * validation context * @return status */ protected IStatus handleTargetPinModification(Pin pin, IValidationContext ctx) { // the type of the target pin can not be modified. if(UMLPackage.eINSTANCE.getTypedElement_Type().equals(ctx.getFeature())) { Element owner = pin.getOwner(); if(owner instanceof CallOperationAction && ((CallOperationAction)owner).getOperation() != null) { proposeNavigation(((CallOperationAction)owner).getOperation()); return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Propose the user to create a parameter in the given element * * @param element * element to navigate to or to create a parameter in * @param preferredPinClass * the direction to select as default (or null) */ protected boolean proposeParameterCreation(final NamedElement element, final EClass preferredPinClass) { final String elementLabel = labelProvider.getText(element); final String message = NLS.bind(Messages.PinAndParameterSynchronizer_UnauthorizedModification, elementLabel); final ParameterDirectionKind preferredDirection; if(UMLPackage.eINSTANCE.getOutputPin().isSuperTypeOf(preferredPinClass)) { preferredDirection = ParameterDirectionKind.OUT_LITERAL; } else { preferredDirection = ParameterDirectionKind.IN_LITERAL; } SafeDialogOpenerDuringValidation<Boolean> opener = new SafeDialogOpenerDuringValidation<Boolean>() { protected Boolean openDialog() { WarningAndCreateParameterDialog dialog = new WarningAndCreateParameterDialog(new Shell(Display.getDefault()), Messages.PinAndParameterSynchronizer_UnauthorizedModificationTitle, message, element, labelProvider, preferredDirection); boolean result = dialog.open() == Window.OK; if(result) { Parameter parameter = dialog.getParameter(); handleParameterCreatedDuringValidation(parameter, preferredPinClass); } return result; } }; return opener.execute(); } /** * Correct the model to add required pins, taking in account the parameter * which has just been created with no validation feedback. * * @param parameter * the created parameter * @param preferredPinClass * the EClass the user would like to create a pin of */ protected void handleParameterCreatedDuringValidation(Parameter parameter, EClass preferredPinClass) { if(parameter != null) { Map<Integer, TypedElement> empty = Collections.emptyMap(); CompoundCommand globalCmd = new CompoundCommand(); // explore referencing actions List<InvocationAction> callingActions = getCallingActions(parameter.getOwner()); switch(parameter.getDirection()) { case IN_LITERAL: for(InvocationAction action : callingActions) { if(action instanceof CallAction) { int index = action.getArguments().size(); CompoundCommand cmd = getAddPinsCmd((CallAction)action, Collections.singletonMap(index, (TypedElement)parameter), empty, preferredPinClass); globalCmd.append(cmd); } } break; case OUT_LITERAL: case RETURN_LITERAL: for(InvocationAction action : callingActions) { if(action instanceof CallAction) { int index = ((CallAction)action).getResults().size(); CompoundCommand cmd = getAddPinsCmd((CallAction)action, empty, Collections.singletonMap(index, (TypedElement)parameter), preferredPinClass); globalCmd.append(cmd); } } break; case INOUT_LITERAL: for(InvocationAction action : callingActions) { if(action instanceof CallAction) { int indexIn = action.getArguments().size(); int indexOut = ((CallAction)action).getResults().size(); CompoundCommand cmd = getAddPinsCmd((CallAction)action, Collections.singletonMap(indexIn, (TypedElement)parameter), Collections.singletonMap(indexOut, (TypedElement)parameter), preferredPinClass); globalCmd.append(cmd); } } break; } if(!globalCmd.isEmpty() && globalCmd.canExecute()) { globalCmd.execute(); } } } /** * Propose the user to create an attribute in the given element * * @param element * element to navigate to or to create an attribute in * @param preferredPinClass * the direction to select as default (or null) */ protected boolean proposeAttributeCreation(final NamedElement element, final EClass preferredPinClass) { final String elementLabel = labelProvider.getText(element); final String message = NLS.bind(Messages.PinAndParameterSynchronizer_UnauthorizedModification, elementLabel); SafeDialogOpenerDuringValidation<Boolean> opener = new SafeDialogOpenerDuringValidation<Boolean>() { protected Boolean openDialog() { WarningAndCreateAttributeDialog dialog = new WarningAndCreateAttributeDialog(new Shell(Display.getDefault()), Messages.PinAndParameterSynchronizer_UnauthorizedModificationTitle, message, element, labelProvider); boolean result = dialog.open() == Window.OK; if(result) { Property attribute = dialog.getAttribute(); handlePropertyCreatedDuringValidation(attribute, preferredPinClass); } return result; } }; return opener.execute(); } /** * Correct the model to add required pins, taking in account the property * which has just been created with no validation feedback. * * @param property * the created property * @param preferredPinClass * the EClass the user would like to create a pin of */ protected void handlePropertyCreatedDuringValidation(Property property, EClass preferredPinClass) { if(property != null) { Map<Integer, Parameter> empty = Collections.emptyMap(); CompoundCommand globalCmd = new CompoundCommand(); // explore referencing actions List<InvocationAction> callingActions = getCallingActions(property.getOwner()); for(InvocationAction action : callingActions) { if(action instanceof SendSignalAction || action instanceof BroadcastSignalActionEditPart) { int index = action.getArguments().size(); CompoundCommand cmd = getAddPinsCmd((SendSignalAction)action, Collections.singletonMap(index, property), preferredPinClass); globalCmd.append(cmd); } } if(!globalCmd.isEmpty() && globalCmd.canExecute()) { globalCmd.execute(); } } } /** * Propose the user to navigate to the given element * * @param element * element to navigate to */ protected void proposeNavigation(final NamedElement element) { final String elementLabel = labelProvider.getText(element); final String message = NLS.bind(Messages.PinAndParameterSynchronizer_UnauthorizedModificationRedirection, elementLabel); SafeDialogOpenerDuringValidation<Void> opener = new SafeDialogOpenerDuringValidation<Void>() { protected Void openDialog() { WarningAndLinkDialog dialog = new WarningAndLinkDialog(new Shell(Display.getDefault()), Messages.PinAndParameterSynchronizer_UnauthorizedModificationTitle, message, element, elementLabel); dialog.open(); return null; } }; opener.execute(); } /** * Ensure Pin modification is in accordance with associated Parameter * * @param pin * modified pin (not a target pin) * @param ctx * validation context * @return status */ protected IStatus handlePinModification(Pin pin, IValidationContext ctx) { if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(testPinOrParameterOrPropertyFeature(ctx.getFeature())) { NamedElement invoked = getInvokedObject(pin); if(invoked != null) { proposeNavigation(invoked); return ctx.createFailureStatus(); } } } return ctx.createSuccessStatus(); } /** * Ensure Parameter modification is reported on associated Pins * * @param parameter * modified parameter * @param ctx * validation context * @return status */ protected IStatus handleParameterModification(Parameter parameter, IValidationContext ctx) { if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(testPinOrParameterOrPropertyFeature(ctx.getFeature())) { // Update corresponding pins CompoundCommand cmd = getUpdatePinsCmd(getPins(parameter), parameter.getType(), parameter.isOrdered(), parameter.isUnique(), parameter.getLower(), parameter.getLowerValue(), parameter.getUpper(), parameter.getUpperValue()); if(!cmd.isEmpty() && cmd.canExecute()) { if(askForValidation(getCallingActions(parameter.getOwner()))) { cmd.execute(); } else { return ctx.createFailureStatus(); } } } else if(UMLPackage.eINSTANCE.getParameter_Direction().equals(ctx.getFeature())) { // Remove/Add corresponding pins with type in accordance to // direction for(Notification event : ctx.getAllEvents()) { if(UMLPackage.eINSTANCE.getParameter_Direction().equals(event.getFeature()) || UMLPackage.eINSTANCE.getBehavioralFeature_OwnedParameter().equals(event.getFeature())) { return changePinsBecauseOfParameterDirection(parameter, event, ctx); } } } else if(UMLPackage.eINSTANCE.getNamedElement_Name().equals(ctx.getFeature())) { // Synchronize the pin name if not set yet CompoundCommand cmd = getSetPinsNamesCmd(getPins(parameter), parameter.getName()); if(!cmd.isEmpty() && cmd.canExecute()) { if(askForValidation(getCallingActions(parameter.getOwner()))) { cmd.execute(); } else { return ctx.createFailureStatus(); } } } } return ctx.createSuccessStatus(); } /** * Ensure Property modification is reported on associated Pins * * @param property * modified property * @param ctx * validation context * @return status */ protected IStatus handlePropertyModification(Property property, IValidationContext ctx) { if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(testPinOrParameterOrPropertyFeature(ctx.getFeature())) { // Update corresponding pins CompoundCommand cmd = getUpdatePinsCmd(getPins(property), property.getType(), property.isOrdered(), property.isUnique(), property.getLower(), property.getLowerValue(), property.getUpper(), property.getUpperValue()); if(!cmd.isEmpty() && cmd.canExecute()) { if(askForValidation(getCallingActions(property.getOwner()))) { cmd.execute(); } else { return ctx.createFailureStatus(); } } } else if(UMLPackage.eINSTANCE.getNamedElement_Name().equals(ctx.getFeature())) { // Synchronize the pin name if not set yet CompoundCommand cmd = getSetPinsNamesCmd(getPins(property), property.getName()); if(!cmd.isEmpty() && cmd.canExecute()) { if(askForValidation(getCallingActions(property.getOwner()))) { cmd.execute(); } else { return ctx.createFailureStatus(); } } } } return ctx.createSuccessStatus(); } /** * Change the Pins because parameter's direction has changed * * @param parameter * the modified parameter * @param event * the direction change event * @param ctx * validation context * @return status */ protected IStatus changePinsBecauseOfParameterDirection(TypedElement parameter, Notification event, IValidationContext ctx) { // constants used for type safety List<Integer> emptyList = Collections.emptyList(); Map<Integer, TypedElement> emptyMap = Collections.emptyMap(); Object oldDir = event.getOldValue(); Object newDir = event.getNewValue(); int inIndex = getIndex(parameter, true); int outIndex = getIndex(parameter, false); CompoundCommand globalCmd = new CompoundCommand(); List<InvocationAction> callingActions = getCallingActions(parameter.getOwner()); if(ParameterDirectionKind.IN_LITERAL.equals(oldDir)) { if(ParameterDirectionKind.OUT_LITERAL.equals(newDir) || ParameterDirectionKind.RETURN_LITERAL.equals(newDir)) { // explore referencing actions to remove in and add out for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getRemovePinsCmd((CallAction)action, Collections.singletonList(inIndex), emptyList); globalCmd.append(cmd); cmd = getAddPinsCmd((CallAction)action, emptyMap, Collections.singletonMap(outIndex, parameter), null); globalCmd.append(cmd); } } } else if(ParameterDirectionKind.INOUT_LITERAL.equals(newDir)) { // explore referencing actions to add out for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getAddPinsCmd((CallAction)action, emptyMap, Collections.singletonMap(outIndex, parameter), null); globalCmd.append(cmd); } } } } else if(ParameterDirectionKind.OUT_LITERAL.equals(oldDir) || ParameterDirectionKind.RETURN_LITERAL.equals(oldDir)) { if(ParameterDirectionKind.IN_LITERAL.equals(newDir)) { // explore referencing actions to remove out and add in for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getRemovePinsCmd((CallAction)action, emptyList, Collections.singletonList(outIndex)); globalCmd.append(cmd); cmd = getAddPinsCmd((CallAction)action, Collections.singletonMap(inIndex, parameter), emptyMap, null); globalCmd.append(cmd); } } } else if(ParameterDirectionKind.INOUT_LITERAL.equals(newDir)) { // explore referencing actions to add in for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getAddPinsCmd((CallAction)action, Collections.singletonMap(inIndex, parameter), emptyMap, null); globalCmd.append(cmd); } } } } else if(ParameterDirectionKind.INOUT_LITERAL.equals(oldDir)) { if(ParameterDirectionKind.IN_LITERAL.equals(newDir)) { // explore referencing actions to remove out for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getRemovePinsCmd((CallAction)action, emptyList, Collections.singletonList(outIndex)); globalCmd.append(cmd); } } } else if(ParameterDirectionKind.OUT_LITERAL.equals(newDir) || ParameterDirectionKind.RETURN_LITERAL.equals(newDir)) { // explore referencing actions to remove in for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getRemovePinsCmd((CallAction)action, Collections.singletonList(inIndex), emptyList); globalCmd.append(cmd); } } } } if(!globalCmd.isEmpty() && globalCmd.canExecute()) { if(askForValidation(callingActions)) { globalCmd.execute(); } else { return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Get the list of actions invoking this element * * @param element * an Operation or a Behavior or a Signal * @return list of InvocationAction */ protected static List<InvocationAction> getCallingActions(Element element) { // explore referencing actions Set<InvocationAction> callingActions = new HashSet<InvocationAction>(); if(element instanceof Behavior || element instanceof Operation) { Collection<Setting> references = CacheAdapter.INSTANCE.getNonNavigableInverseReferences(element); for(Setting ref : references) { EObject action = ref.getEObject(); // parameter's owner is action's called Operation boolean operationCase = UMLPackage.eINSTANCE.getCallOperationAction_Operation().equals(ref.getEStructuralFeature()); // parameter's owner is action's called Behavior boolean behaviorCase = UMLPackage.eINSTANCE.getCallBehaviorAction_Behavior().equals(ref.getEStructuralFeature()); if((operationCase || behaviorCase) && action instanceof CallAction && action.eContainer() != null) { callingActions.add((CallAction)action); } } } else if(element instanceof Signal) { Collection<Setting> references = CacheAdapter.INSTANCE.getNonNavigableInverseReferences(element); for(Setting ref : references) { EObject action = ref.getEObject(); // parameter's owner is action's sent Signal boolean signalCase = UMLPackage.eINSTANCE.getSendSignalAction_Signal().equals(ref.getEStructuralFeature()); if(signalCase && action instanceof SendSignalAction && action.eContainer() != null) { callingActions.add((SendSignalAction)action); } } } return new ArrayList<InvocationAction>(callingActions); } /** * Ensure CallOperationAction modification is reported on associated * Operation * * @param action * modified action * @param ctx * validation context * @return status */ protected IStatus handleCallOperationActionModification(CallOperationAction action, IValidationContext ctx) { if(testTransformPinCase(ctx)) { return ctx.createSuccessStatus(); } else if(testCustomModificationToValidPins(action, ctx)) { return ctx.createSuccessStatus(); } else if(EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getOperation() != null) { if(action.getOperation() != null) { if(canCreateParameterFromCallAction(action)) { Object pin = ctx.getFeatureNewValue(); boolean parameterCreated = proposeParameterCreation(action.getOperation(), ((EObject)pin).eClass()); if(parameterCreated) { // remove the user-created value TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); Command cmd = RemoveCommand.create(editingdomain, ctx.getFeatureNewValue()); cmd.execute(); return ctx.createSuccessStatus(); } } else { /* * No modification of parameters is allowed from the * CallOperationAction. This means we can not add Pins */ proposeNavigation(action.getOperation()); } return ctx.createFailureStatus(); } } } else if(EMFEventType.REMOVE.equals(ctx.getEventType()) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getOperation() != null) { /* * Yet, no modification of parameters is allowed from the * CallOperationAction. This means we can not remove Pins */ if(action.getOperation() != null) { proposeNavigation(action.getOperation()); return ctx.createFailureStatus(); } } } else if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getCallOperationAction_Operation().equals(ctx.getFeature())) { /* * The operation changes, so must the pins */ CompoundCommand cmd = getResetPinsCmd(action); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } } if(UMLPackage.eINSTANCE.getCallOperationAction_Target().equals(ctx.getFeature())) { /* * Try to remove or assign target pin. This must not be * authorized. */ final String msg = NLS.bind(Messages.PinAndParameterSynchronizer_UndeleteablePinMessage, UMLPackage.eINSTANCE.getCallOperationAction_Target().getName()); SafeDialogOpenerDuringValidation<Void> opener = new SafeDialogOpenerDuringValidation<Void>() { protected Void openDialog() { MessageDialog.openWarning(new Shell(Display.getDefault()), Messages.PinAndParameterSynchronizer_UndeleteablePinTitle, msg); return null; } }; opener.execute(); return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * @param node * @param ctx * @return */ protected boolean testCustomModificationToValidPins(ActivityNode node, IValidationContext ctx) { // reflexive code to call each validation method matching the given object in : UMLValidationHelper.class // the method call rach public and static method IStatus status = null; Class<? extends UMLValidationHelper> aClass = UMLValidationHelper.class; Method[] methods = aClass.getDeclaredMethods(); for(Method m : methods) { if(Modifier.isStatic(m.getModifiers()) && Modifier.isPublic(m.getModifiers()) && m.getReturnType() == IStatus.class) { if(m.isAnnotationPresent(PinAndParameterSynchronizeValidator.class) && m.getParameterTypes().length == 2) { if(m.getParameterTypes()[0].isInstance(node)) { try { status = (IStatus)m.invoke(aClass, node, ctx); if(status != null && !status.isOK()) { break; } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } } return status == null || status.isOK(); } /** * Ensure CallBehaviorAction modification is reported on associated Behavior * * @param action * modified action * @param ctx * validation context * @return status */ protected IStatus handleCallBehaviorActionModification(CallBehaviorAction action, IValidationContext ctx) { if(testTransformPinCase(ctx)) { return ctx.createSuccessStatus(); } else if(EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getBehavior() != null) { if(action.getBehavior() != null) { if(canCreateParameterFromCallAction(action)) { Object pin = ctx.getFeatureNewValue(); boolean parameterCreated = proposeParameterCreation(action.getBehavior(), ((EObject)pin).eClass()); if(parameterCreated) { // remove the user-created value TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); Command cmd = RemoveCommand.create(editingdomain, ctx.getFeatureNewValue()); cmd.execute(); return ctx.createSuccessStatus(); } } else { /* * No modification of parameters is allowed from the * CallBehaviorAction. This means we can not add Pins */ proposeNavigation(action.getBehavior()); } return ctx.createFailureStatus(); } } } else if(EMFEventType.REMOVE.equals(ctx.getEventType()) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getBehavior() != null) { /* * Yet, no modification of parameters is allowed from the * CallBehaviorAction. This means we can not remove Pins */ if(action.getBehavior() != null) { proposeNavigation(action.getBehavior()); return ctx.createFailureStatus(); } } } else if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getCallBehaviorAction_Behavior().equals(ctx.getFeature())) { /* * The behavior changes, so must the pins */ CompoundCommand cmd = getResetPinsCmd(action); if(action.getBehavior() != null) { String behaviorName = action.getBehavior().getName(); if(behaviorName != null && !"".equals(behaviorName)) { // By the way, update the CallBehaviorAction name TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); cmd.append(SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getNamedElement_Name(), behaviorName)); } } if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } } } return ctx.createSuccessStatus(); } /** * Test if a parameter can be created from a call action, after appropriate * warnings * * @param action * the call action * @return true if we can create a parameter */ protected boolean canCreateParameterFromCallAction(CallAction action) { return true; } /** * Test if an attribute can be created from a send signal action, after * appropriate warnings * * @param action * the send signal action * @return true if we can create an attribute */ protected boolean canCreateAttributesFromSendSignalAction(SendSignalAction action) { return true; } /** * Ensure BroadcastSignalAction modification is reported on associated * Signal * * @param action * modified action * @param ctx * validation context * @return status */ protected IStatus handleBroadcastSignalActionModification(BroadcastSignalAction action, IValidationContext ctx) { if(testTransformPinCase(ctx)) { return ctx.createSuccessStatus(); } else if(EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getSignal() != null) { if(action.getSignal() != null) { Object pin = ctx.getFeatureNewValue(); boolean attributeCreated = proposeAttributeCreation(action.getSignal(), ((EObject)pin).eClass()); if(attributeCreated) { // remove the user-created value TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); Command cmd = RemoveCommand.create(editingdomain, ctx.getFeatureNewValue()); cmd.execute(); return ctx.createSuccessStatus(); } return ctx.createFailureStatus(); } } } else if(EMFEventType.REMOVE.equals(ctx.getEventType()) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getSignal() != null) { /* * Yet, no modification of attributes is allowed from the * BroadcastSignalAction. This means we can not remove Pins */ if(action.getSignal() != null) { proposeNavigation(action.getSignal()); return ctx.createFailureStatus(); } } } else if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getBroadcastSignalAction_Signal().equals(ctx.getFeature())) { /* * The signal changes, so must the pins */ CompoundCommand cmd = getResetPinsCmd(action); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } } } return ctx.createSuccessStatus(); } /** * Ensure SendSignalAction modification is reported on associated Signal * * @param action * modified action * @param ctx * validation context * @return status */ protected IStatus handleSendSignalActionModification(SendSignalAction action, IValidationContext ctx) { if(testTransformPinCase(ctx)) { return ctx.createSuccessStatus(); } else if(EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getSignal() != null) { if(action.getSignal() != null) { if(canCreateAttributesFromSendSignalAction(action)) { Object pin = ctx.getFeatureNewValue(); boolean attributeCreated = proposeAttributeCreation(action.getSignal(), ((EObject)pin).eClass()); if(attributeCreated) { // remove the user-created value TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); Command cmd = RemoveCommand.create(editingdomain, ctx.getFeatureNewValue()); cmd.execute(); return ctx.createSuccessStatus(); } } else { /* * No modification of attributes is allowed from the * SendSignalAction. This means we can not add Pins */ proposeNavigation(action.getSignal()); } return ctx.createFailureStatus(); } } } else if(EMFEventType.REMOVE.equals(ctx.getEventType()) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType())) { if(testActionFeature(ctx.getFeature()) && action.getSignal() != null) { /* * Yet, no modification of attributes is allowed from the * SendSignalAction. This means we can not remove Pins */ if(action.getSignal() != null) { proposeNavigation(action.getSignal()); return ctx.createFailureStatus(); } } } else if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getSendSignalAction_Signal().equals(ctx.getFeature())) { /* * The signal changes, so must the pins */ CompoundCommand cmd = getResetPinsCmd(action); if(!cmd.isEmpty() && cmd.canExecute()) { cmd.execute(); } } if(UMLPackage.eINSTANCE.getSendSignalAction_Target().equals(ctx.getFeature())) { /* * Try to remove or assign target pin. This must not be * authorized. */ final String msg = NLS.bind(Messages.PinAndParameterSynchronizer_UndeleteablePinMessage, UMLPackage.eINSTANCE.getSendSignalAction_Target().getName()); SafeDialogOpenerDuringValidation<Void> opener = new SafeDialogOpenerDuringValidation<Void>() { protected Void openDialog() { MessageDialog.openWarning(new Shell(Display.getDefault()), Messages.PinAndParameterSynchronizer_UndeleteablePinTitle, msg); return null; } }; opener.execute(); return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Ensure SendObjectAction modification is authorized * * @param action * modified action * @param ctx * validation context * @return status */ protected IStatus handleSendObjectActionModification(SendObjectAction action, IValidationContext ctx) { if(testTransformPinCase(ctx)) { return ctx.createSuccessStatus(); } else if(EMFEventType.SET.equals(ctx.getEventType()) || EMFEventType.UNSET.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getSendObjectAction_Target().equals(ctx.getFeature())) { /* * Try to remove or assign target pin. This must not be * authorized. */ final String msg = NLS.bind(Messages.PinAndParameterSynchronizer_UndeleteablePinMessage, UMLPackage.eINSTANCE.getSendObjectAction_Target().getName()); SafeDialogOpenerDuringValidation<Void> opener = new SafeDialogOpenerDuringValidation<Void>() { protected Void openDialog() { MessageDialog.openWarning(new Shell(Display.getDefault()), Messages.PinAndParameterSynchronizer_UndeleteablePinTitle, msg); return null; } }; opener.execute(); return ctx.createFailureStatus(); } if(UMLPackage.eINSTANCE.getSendObjectAction_Request().equals(ctx.getFeature())) { /* * Try to remove or assign target pin. This must not be * authorized. */ final String msg = NLS.bind(Messages.PinAndParameterSynchronizer_UndeleteablePinMessage, UMLPackage.eINSTANCE.getSendObjectAction_Request().getName()); SafeDialogOpenerDuringValidation<Void> opener = new SafeDialogOpenerDuringValidation<Void>() { protected Void openDialog() { MessageDialog.openWarning(new Shell(Display.getDefault()), Messages.PinAndParameterSynchronizer_UndeleteablePinTitle, msg); return null; } }; opener.execute(); return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Test whether the executing operation is a transform into on a pin * * @param ctx * the validation context * @return true if we are executing a transform into on a pin */ protected boolean testTransformPinCase(IValidationContext ctx) { List<Notification> events = ctx.getAllEvents(); Object removed = null; Object added = null; if(events.size() == 1) { Notification set = events.get(0); if(Notification.SET == set.getEventType()) { removed = set.getOldValue(); added = set.getNewValue(); } } else if(events.size() == 2) { Notification remove = events.get(0); Notification add = events.get(1); if(Notification.REMOVE == remove.getEventType() && Notification.ADD == add.getEventType()) { if(remove.getPosition() == add.getPosition() && add.getFeature().equals(remove.getFeature())) { removed = remove.getOldValue(); added = add.getNewValue(); } } else if(Notification.REMOVE_MANY == remove.getEventType() && Notification.ADD_MANY == add.getEventType()) { if(Notification.NO_INDEX == remove.getPosition() && add.getFeature().equals(remove.getFeature())) { if(remove.getOldValue() instanceof List<?> && add.getNewValue() instanceof List<?>) { ArrayList<Object> removeList = new ArrayList<Object>((List<?>)remove.getOldValue()); removeList.removeAll((List<?>)add.getNewValue()); ArrayList<Object> addList = new ArrayList<Object>((List<?>)add.getNewValue()); addList.removeAll((List<?>)remove.getOldValue()); if(removeList.size() == 1 && addList.size() == 1) { removed = removeList.get(0); added = addList.get(0); } } } } } // check that replacing pin object is similar before concluding a // transform into if(removed instanceof Pin && added instanceof Pin) { Pin removedPin = (Pin)removed; Pin addedPin = (Pin)added; boolean similars = true; // test name similars &= removedPin.getName() == null || removedPin.getName().equals(addedPin.getName()); // test type similars &= EcoreUtil.equals(removedPin.getType(), addedPin.getType()); // test is ordered similars &= removedPin.isOrdered() == addedPin.isOrdered(); // test multiplicity : is unique similars &= removedPin.isUnique() == addedPin.isUnique(); // test multiplicity : lower value similars &= EcoreUtil.equals(removedPin.getLowerValue(), addedPin.getLowerValue()); // test multiplicity : upper value similars &= EcoreUtil.equals(removedPin.getUpperValue(), addedPin.getUpperValue()); return similars; } return false; } /** * Ensure Operation modification is reported on associated actions * * @param operation * modified operation * @param ctx * validation context * @return status */ protected IStatus handleOperationModification(Operation operation, IValidationContext ctx) { if(EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getBehavioralFeature_OwnedParameter().equals(ctx.getFeature())) { return handleParametersAdded(operation, ctx); } } else if(EMFEventType.REMOVE.equals(ctx.getEventType()) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getBehavioralFeature_OwnedParameter().equals(ctx.getFeature())) { return handleParametersRemoved(operation, ctx); } } return ctx.createSuccessStatus(); } /** * Ensure Behavior modification is reported on associated actions * * @param behavior * modified behavior * @param ctx * validation context * @return status */ protected IStatus handleBehaviorModification(Behavior behavior, IValidationContext ctx) { if(EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getBehavior_OwnedParameter().equals(ctx.getFeature())) { return handleParametersAdded(behavior, ctx); } } else if(EMFEventType.REMOVE.equals(ctx.getEventType()) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getBehavior_OwnedParameter().equals(ctx.getFeature())) { return handleParametersRemoved(behavior, ctx); } } return ctx.createSuccessStatus(); } /** * Ensure Signal modification is reported on associated actions * * @param signal * modified signal * @param ctx * validation context * @return status */ protected IStatus handleSignalModification(Signal signal, IValidationContext ctx) { if(EMFEventType.ADD.equals(ctx.getEventType()) || EMFEventType.ADD_MANY.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getSignal_OwnedAttribute().equals(ctx.getFeature())) { return handleAttributesAdded(signal, ctx); } } else if(EMFEventType.REMOVE.equals(ctx.getEventType()) || EMFEventType.REMOVE_MANY.equals(ctx.getEventType())) { if(UMLPackage.eINSTANCE.getSignal_OwnedAttribute().equals(ctx.getFeature())) { return handleAttributesRemoved(signal, ctx); } } return ctx.createSuccessStatus(); } /** * Ensure Pins corresponding to parameters are removed * * @param element * modified operation or behavior * @param ctx * validation context * @return status */ protected IStatus handleParametersRemoved(NamedElement element, IValidationContext ctx) { // construct the list of removed indexes and their direction Map<Integer, ParameterDirectionKind> removedParameterIndexes = new HashMap<Integer, ParameterDirectionKind>(); for(Notification event : ctx.getAllEvents()) { if(UMLPackage.eINSTANCE.getBehavior_OwnedParameter().equals(event.getFeature()) || UMLPackage.eINSTANCE.getBehavioralFeature_OwnedParameter().equals(event.getFeature())) { Object removedValue = event.getOldValue(); if(removedValue instanceof Parameter) { // handle parameter direction ParameterDirectionKind dir = ((Parameter)removedValue).getDirection(); removedParameterIndexes.put(event.getPosition(), dir); } else if(removedValue instanceof List<?>) { List<?> col = (List<?>)removedValue; if(!col.isEmpty()) { for(int i = 0; i < col.size(); i++) { Object object = col.get(i); if(object instanceof Parameter) { removedParameterIndexes.put(i, ((Parameter)object).getDirection()); } } } } } } List<Parameter> newParameters = Collections.emptyList(); if(element instanceof Behavior) { newParameters = ((Behavior)element).getOwnedParameters(); } else if(element instanceof Operation) { newParameters = ((Operation)element).getOwnedParameters(); } List<Integer> removedInputPinIndexes = new LinkedList<Integer>(); List<Integer> removedOutputPinIndexes = new LinkedList<Integer>(); Iterator<Parameter> parametersIterator = newParameters.iterator(); // iterate on the virtual list of old parameters // (correspondingParameterIndex) to deduce // pins indexes int correspondingParameterIndex = 0; int correspondingInputPinIndex = 0; int correspondingOutputPinIndex = 0; while(removedParameterIndexes.containsKey(correspondingParameterIndex) || parametersIterator.hasNext()) { if(removedParameterIndexes.containsKey(correspondingParameterIndex)) { // parameter removed, pin(s) removed switch(removedParameterIndexes.get(correspondingParameterIndex)) { case IN_LITERAL: removedInputPinIndexes.add(correspondingInputPinIndex); correspondingInputPinIndex++; break; case OUT_LITERAL: case RETURN_LITERAL: removedOutputPinIndexes.add(correspondingOutputPinIndex); correspondingOutputPinIndex++; break; case INOUT_LITERAL: // in-out parameter has two pins removedInputPinIndexes.add(correspondingInputPinIndex); correspondingInputPinIndex++; removedOutputPinIndexes.add(correspondingOutputPinIndex); correspondingOutputPinIndex++; break; } } else { // parameter not removed, pin(s) not removed Parameter nextParam = parametersIterator.next(); switch(nextParam.getDirection()) { case IN_LITERAL: correspondingInputPinIndex++; break; case OUT_LITERAL: case RETURN_LITERAL: correspondingOutputPinIndex++; break; case INOUT_LITERAL: // in-out parameter has two pins correspondingInputPinIndex++; correspondingOutputPinIndex++; break; } } // explore next parameter correspondingParameterIndex++; } CompoundCommand globalCmd = new CompoundCommand(); // explore referencing actions List<InvocationAction> callingActions = getCallingActions(element); for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getRemovePinsCmd((CallAction)action, removedInputPinIndexes, removedOutputPinIndexes); globalCmd.append(cmd); } } if(!globalCmd.isEmpty() && globalCmd.canExecute()) { if(askForValidation(callingActions)) { globalCmd.execute(); } else { return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Ensure Pins corresponding to parameters are added * * @param element * modified operation or behavior * @param ctx * validation context * @return status */ protected IStatus handleParametersAdded(NamedElement element, IValidationContext ctx) { // construct the list of added indexes and their direction List<Parameter> addedParameters = new LinkedList<Parameter>(); for(Notification event : ctx.getAllEvents()) { if(UMLPackage.eINSTANCE.getBehavior_OwnedParameter().equals(event.getFeature()) || UMLPackage.eINSTANCE.getBehavioralFeature_OwnedParameter().equals(event.getFeature())) { Object addedValue = event.getNewValue(); if(addedValue instanceof Parameter) { addedParameters.add((Parameter)addedValue); } else if(addedValue instanceof List<?>) { List<?> col = (List<?>)addedValue; if(!col.isEmpty()) { for(int i = 0; i < col.size(); i++) { Object object = col.get(i); if(object instanceof Parameter) { addedParameters.add((Parameter)object); } } } } } } List<Parameter> newParameters = Collections.emptyList(); if(element instanceof Behavior) { newParameters = ((Behavior)element).getOwnedParameters(); } else if(element instanceof Operation) { newParameters = ((Operation)element).getOwnedParameters(); } Map<Integer, TypedElement> addedInputPinMap = new HashMap<Integer, TypedElement>(); Map<Integer, TypedElement> addedOutputPinMap = new HashMap<Integer, TypedElement>(); // iterate on the list of new parameters to deduce pins indexes int correspondingInputPinIndex = 0; int correspondingOutputPinIndex = 0; for(Parameter param : newParameters) { if(addedParameters.contains(param)) { // parameter added, pin(s) to add switch(param.getDirection()) { case IN_LITERAL: addedInputPinMap.put(correspondingInputPinIndex, param); correspondingInputPinIndex++; break; case OUT_LITERAL: case RETURN_LITERAL: addedOutputPinMap.put(correspondingOutputPinIndex, param); correspondingOutputPinIndex++; break; case INOUT_LITERAL: // in-out parameter has two pins addedInputPinMap.put(correspondingInputPinIndex, param); correspondingInputPinIndex++; addedOutputPinMap.put(correspondingOutputPinIndex, param); correspondingOutputPinIndex++; break; } } else { // parameter not added, pin already exists switch(param.getDirection()) { case IN_LITERAL: correspondingInputPinIndex++; break; case OUT_LITERAL: case RETURN_LITERAL: correspondingOutputPinIndex++; break; case INOUT_LITERAL: // in-out parameter has two pins correspondingInputPinIndex++; correspondingOutputPinIndex++; break; } } // explore next parameter } CompoundCommand globalCmd = new CompoundCommand(); // explore referencing actions List<InvocationAction> callingActions = getCallingActions(element); for(InvocationAction action : callingActions) { if(action instanceof CallAction) { CompoundCommand cmd = getAddPinsCmd((CallAction)action, addedInputPinMap, addedOutputPinMap, null); globalCmd.append(cmd); } } if(!globalCmd.isEmpty() && globalCmd.canExecute()) { if(askForValidation(callingActions)) { globalCmd.execute(); } else { return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Ensure Pins corresponding to parameters are removed * * @param element * modified signal * @param ctx * validation context * @return status */ protected IStatus handleAttributesRemoved(Signal element, IValidationContext ctx) { // construct the list of removed indexes and their direction Set<Integer> removedAttributeIndexes = new HashSet<Integer>(); for(Notification event : ctx.getAllEvents()) { if(UMLPackage.eINSTANCE.getSignal_OwnedAttribute().equals(event.getFeature())) { Object removedValue = event.getOldValue(); if(removedValue instanceof Property) { removedAttributeIndexes.add(event.getPosition()); } else if(removedValue instanceof List<?>) { List<?> col = (List<?>)removedValue; if(!col.isEmpty()) { for(int i = 0; i < col.size(); i++) { Object object = col.get(i); if(object instanceof Property) { removedAttributeIndexes.add(i); } } } } } } // deduce pins indexes from old attributes indexes List<Integer> removedInputPinIndexes = new LinkedList<Integer>(removedAttributeIndexes); CompoundCommand globalCmd = new CompoundCommand(); // explore referencing actions List<InvocationAction> callingActions = getCallingActions(element); for(InvocationAction action : callingActions) { if(action instanceof SendSignalAction) { CompoundCommand cmd = getRemovePinsCmd((SendSignalAction)action, removedInputPinIndexes); globalCmd.append(cmd); } } if(!globalCmd.isEmpty() && globalCmd.canExecute()) { if(askForValidation(callingActions)) { globalCmd.execute(); } else { return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Ensure Pins corresponding to attributes are added * * @param element * modified signal * @param ctx * validation context * @return status */ protected IStatus handleAttributesAdded(Signal element, IValidationContext ctx) { // construct the list of added indexes and their direction List<Property> addedAttributes = new LinkedList<Property>(); for(Notification event : ctx.getAllEvents()) { if(UMLPackage.eINSTANCE.getSignal_OwnedAttribute().equals(event.getFeature())) { Object addedValue = event.getNewValue(); if(addedValue instanceof Property) { addedAttributes.add((Property)addedValue); } else if(addedValue instanceof List<?>) { List<?> col = (List<?>)addedValue; if(!col.isEmpty()) { for(int i = 0; i < col.size(); i++) { Object object = col.get(i); if(object instanceof Property) { addedAttributes.add((Property)object); } } } } } } List<Property> newAttributes = Collections.emptyList(); if(element instanceof Signal) { newAttributes = ((Signal)element).getOwnedAttributes(); } Map<Integer, Property> addedInputPinMap = new HashMap<Integer, Property>(); // iterate on the list of added attributes to deduce pins indexes for(Property param : addedAttributes) { if(newAttributes.contains(param)) { addedInputPinMap.put(newAttributes.indexOf(param), param); } } CompoundCommand globalCmd = new CompoundCommand(); // explore referencing actions List<InvocationAction> callingActions = getCallingActions(element); for(InvocationAction action : callingActions) { if(action instanceof SendSignalAction) { CompoundCommand cmd = getAddPinsCmd((SendSignalAction)action, addedInputPinMap, null); globalCmd.append(cmd); } } if(!globalCmd.isEmpty() && globalCmd.canExecute()) { if(askForValidation(callingActions)) { globalCmd.execute(); } else { return ctx.createFailureStatus(); } } return ctx.createSuccessStatus(); } /** * Get the command to update type of target input pins referring the * operation * * @param operation * the operation which parent type has changed * @return the command */ protected CompoundCommand getUpdateTargetPinsType(Operation operation) { CompoundCommand globalCmd = new CompoundCommand(); // get operation parent type Type type = null; Element owner = operation.getOwner(); if(owner instanceof Type) { type = (Type)owner; } if(type != null) {// Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); // explore referencing actions for(InvocationAction action : getCallingActions(operation)) {// operation // is // action's // called // Operation if(action instanceof CallOperationAction) { InputPin targetPin = ((CallOperationAction)action).getTarget(); if(targetPin != null) { Command cmd = SetCommand.create(editingdomain, targetPin, UMLPackage.eINSTANCE.getTypedElement_Type(), type); globalCmd.append(cmd); } } } } return globalCmd; } /** * Get the command to remove pins linked with parameter at the given indexes * * @param action * the CallOperationAction or CallBehaviorAction (no effect * otherwise) * @param removedInputPinsIndexes * the indexes of input pins to remove (except target) * @param removedOutputPinsIndexes * the indexes of output pins to remove * @return the command to remove corresponding Pins */ protected CompoundCommand getRemovePinsCmd(CallAction action, List<Integer> removedInputPinsIndexes, List<Integer> removedOutputPinsIndexes) { CompoundCommand globalCmd = new CompoundCommand(); // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); if(action instanceof CallBehaviorAction || action instanceof CallOperationAction) { ArrayList<InputPin> removedIn = new ArrayList<InputPin>(removedInputPinsIndexes.size()); for(int i : removedInputPinsIndexes) { if(i < action.getArguments().size()) { removedIn.add(action.getArguments().get(i)); } } if(!removedIn.isEmpty()) { Command cmd = RemoveCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getInvocationAction_Argument(), removedIn); globalCmd.append(cmd); } ArrayList<OutputPin> removedOut = new ArrayList<OutputPin>(removedInputPinsIndexes.size()); for(int i : removedOutputPinsIndexes) { if(i < action.getResults().size()) { removedOut.add(action.getResults().get(i)); } } if(!removedOut.isEmpty()) { Command cmd = RemoveCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getCallAction_Result(), removedOut); globalCmd.append(cmd); } } return globalCmd; } /** * Get the command to remove pins linked with attribute at the given indexes * * @param action * the SendSignalAction * @param removedInputPinsIndexes * the indexes of input pins to remove (except target) * @return the command to remove corresponding Pins */ protected CompoundCommand getRemovePinsCmd(SendSignalAction action, List<Integer> removedInputPinsIndexes) { CompoundCommand globalCmd = new CompoundCommand(); // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); if(action instanceof SendSignalAction) { ArrayList<InputPin> removedIn = new ArrayList<InputPin>(removedInputPinsIndexes.size()); for(int i : removedInputPinsIndexes) { if(i < action.getArguments().size()) { removedIn.add(action.getArguments().get(i)); } } if(!removedIn.isEmpty()) { Command cmd = RemoveCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getInvocationAction_Argument(), removedIn); globalCmd.append(cmd); } } return globalCmd; } /** * Get the command to add pins linked with parameter at the given indexes * * @param invocationAction * the CallBehaviorAction or CallOperationAction (no effect * otherwise) * @param addedInputPinMap * the indexes of input pins to add and parameters to copy * @param addedOutputPinMap * the indexes of output pins to add and parameters to copy * @param preferredPinClass * the EClass to use to create a new pin whenever possible (or * null) * @return the command to add corresponding Pins */ protected static CompoundCommand getAddPinsCmd(InvocationAction invocationAction, Map<Integer, TypedElement> addedInputPinMap, Map<Integer, TypedElement> addedOutputPinMap, EClass preferredPinClass) { CompoundCommand globalCmd = new CompoundCommand(); // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); if(invocationAction instanceof CallBehaviorAction || invocationAction instanceof CallOperationAction || invocationAction instanceof SendSignalAction || invocationAction instanceof BroadcastSignalAction) { /* * An element can not be added at an index bigger than the size. * Hence, add commands must be sorted according to the decreasing * index and the index must be adapted taking in account elements * that will be added. We use a bucket sort on indexes of both maps. */ int nextKey = addedInputPinMap.size() + invocationAction.getArguments().size(); int numberOfPinsToAdd = addedInputPinMap.size(); while(numberOfPinsToAdd > 0) { if(addedInputPinMap.containsKey(nextKey)) { numberOfPinsToAdd--; InputPin pin = createInputPin(addedInputPinMap.get(nextKey), preferredPinClass); // index at which pin is added must take in account other // pins added after int addIndex = nextKey - numberOfPinsToAdd; Command cmd = AddCommand.create(editingdomain, invocationAction, UMLPackage.eINSTANCE.getInvocationAction_Argument(), pin, addIndex); LinkPinToParameter link = new LinkPinToParameter(pin, addedInputPinMap.get(nextKey)); CreatePinToParameterLinkEAnnotation linkCommand = new CreatePinToParameterLinkEAnnotation(EditorUtils.getTransactionalEditingDomain(), link); globalCmd.append(cmd); if(linkCommand != null && linkCommand.canExecute()) { globalCmd.append(linkCommand); } } nextKey--; } if(invocationAction instanceof CallAction) { CallAction callAction = (CallAction)invocationAction; nextKey = addedOutputPinMap.size() + callAction.getResults().size(); numberOfPinsToAdd = addedOutputPinMap.size(); while(numberOfPinsToAdd > 0) { if(addedOutputPinMap.containsKey(nextKey)) { numberOfPinsToAdd--; OutputPin pin = createOutputPin(addedOutputPinMap.get(nextKey)); // index at which pin is added must take in account other // pins added after int addIndex = nextKey - numberOfPinsToAdd; Command cmd = AddCommand.create(editingdomain, callAction, UMLPackage.eINSTANCE.getCallAction_Result(), pin, addIndex); globalCmd.append(cmd); LinkPinToParameter link = new LinkPinToParameter(pin, addedOutputPinMap.get(nextKey)); CreatePinToParameterLinkEAnnotation linkCommand = new CreatePinToParameterLinkEAnnotation(EditorUtils.getTransactionalEditingDomain(), link); if(linkCommand != null) { globalCmd.append(linkCommand); } } nextKey--; } } } return globalCmd; } /** * Get the command to add pins linked with properties at the given indexes * * @param action * the SendSignalAction * @param addedInputPinMap * the indexes of input pins to add and properties to copy * @param preferredPinClass * the EClass to use to create a new pin whenever possible (or * null) * @return the command to add corresponding Pins */ public static CompoundCommand getAddPinsCmd(InvocationAction action, Map<Integer, Property> addedInputPinMap, EClass preferredPinClass) { CompoundCommand globalCmd = new CompoundCommand(); // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); /* * An element can not be added at an index bigger than the size. Hence, * add commands must be sorted according to the decreasing index and the * index must be adapted taking in account elements that will be added. * We use a bucket sort on indexes of both maps. */ int nextKey = addedInputPinMap.size() + action.getArguments().size(); int numberOfPinsToAdd = addedInputPinMap.size(); while(numberOfPinsToAdd > 0) { if(addedInputPinMap.containsKey(nextKey)) { numberOfPinsToAdd--; InputPin pin = createInputPin(addedInputPinMap.get(nextKey), preferredPinClass); // index at which pin is added must take in account other pins // added after int addIndex = nextKey - numberOfPinsToAdd; Command cmd = AddCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getInvocationAction_Argument(), pin, addIndex); globalCmd.append(cmd); LinkPinToParameter link = new LinkPinToParameter(pin, addedInputPinMap.get(nextKey)); CreatePinToParameterLinkEAnnotation linkCommand = new CreatePinToParameterLinkEAnnotation(WorkspaceEditingDomainFactory.INSTANCE.getEditingDomain(action.eResource().getResourceSet()), link); if(linkCommand != null && linkCommand.canExecute()) { globalCmd.append(linkCommand); } } nextKey--; } return globalCmd; } /** * Create an output pin with valued copied from the parameter * * @param typedElement * the reference parameter */ protected static OutputPin createOutputPin(TypedElement typedElement) { OutputPin pin = UMLFactory.eINSTANCE.createOutputPin(); assignUpperBound(pin); // Initialize name pin.setName(typedElement.getName()); // Synchronize type pin.setType(typedElement.getType()); if(typedElement instanceof Property) { Property property = (Property)typedElement; // Synchronize is ordered pin.setIsOrdered(property.isOrdered()); // Synchronize mutliplicity : is unique pin.setIsUnique(property.isUnique()); // Synchronize mutliplicity : lower value ValueSpecification lowerValue = property.getLowerValue(); if(lowerValue != null) { // use a copy command for new value Command copy = CopyCommand.create(EditorUtils.getTransactionalEditingDomain(), Collections.singleton(lowerValue)); copy.execute(); Collection<?> result = copy.getResult(); Object valueToAffect = null; if(!result.isEmpty()) { valueToAffect = result.iterator().next(); } if(valueToAffect instanceof ValueSpecification) { pin.setLowerValue((ValueSpecification)valueToAffect); } } // Synchronize mutliplicity : upper value ValueSpecification upperValue = property.getUpperValue(); if(upperValue != null) { // use a copy command for new value Command copy = CopyCommand.create(EditorUtils.getTransactionalEditingDomain(), Collections.singleton(upperValue)); copy.execute(); Collection<?> result = copy.getResult(); Object valueToAffect = null; if(!result.isEmpty()) { valueToAffect = result.iterator().next(); } if(valueToAffect instanceof ValueSpecification) { pin.setUpperValue((ValueSpecification)valueToAffect); } } } return pin; } /** * Create a target input pin, eventually from a given operation * * @param operation * the invoked operation or null */ protected static InputPin createTargetPin(Operation operation) { InputPin pin = UMLFactory.eINSTANCE.createInputPin(); assignUpperBound(pin); if(operation != null) { Element owningType = operation.getOwner(); if(owningType instanceof Type) { pin.setType((Type)owningType); } } pin.setName(TARGET_PIN_INITIALIZATION_NAME); return pin; } /** * Create a result input pin, eventually from a given operation * * @param classifier * the to set the output type */ protected OutputPin createResultPin(Classifier classifier) { OutputPin pin = UMLFactory.eINSTANCE.createOutputPin(); assignUpperBound(pin); if(classifier != null) { pin.setType(classifier); } pin.setName(RESULT_PIN_INITIALIZATION_NAME); return pin; } /** * Create a result input pin, eventually from a given operation * * @param var * the to set the output type */ protected OutputPin createResultPin(Variable var) { OutputPin pin = UMLFactory.eINSTANCE.createOutputPin(); assignUpperBound(pin); if(var != null) { pin.setType(var.getType()); } pin.setName(RESULT_IN_READ_VARIABLE_ACTION); return pin; } /** * Create a request input pin * * @param operation * the invoked operation or null */ public static InputPin createRequestPin() { InputPin pin = UMLFactory.eINSTANCE.createInputPin(); assignUpperBound(pin); pin.setName(REQUEST_PIN_INITIALIZATION_NAME); return pin; } /** * Create an input pin with valued copied from the property * * @param typedElement * the reference property * @param preferredPinClass * the EClass to use to create a new pin whenever possible (or * null) */ public static InputPin createInputPin(TypedElement typedElement, EClass preferredPinClass) { InputPin pin; if(UMLPackage.eINSTANCE.getValuePin().equals(preferredPinClass)) { pin = UMLFactory.eINSTANCE.createValuePin(); } else if(UMLPackage.eINSTANCE.getActionInputPin().equals(preferredPinClass)) { pin = UMLFactory.eINSTANCE.createActionInputPin(); } else { pin = UMLFactory.eINSTANCE.createInputPin(); } assignUpperBound(pin); // Initialize name pin.setName(typedElement.getName()); // Synchronize type pin.setType(typedElement.getType()); if(pin instanceof Property) { Property property = (Property)pin; // Synchronize is ordered pin.setIsOrdered(property.isOrdered()); // Synchronize multiplicity : is unique pin.setIsUnique(property.isUnique()); // Synchronize multiplicity : lower value ValueSpecification lowerValue = property.getLowerValue(); if(lowerValue != null) { // use a copy command for new value Command copy = CopyCommand.create(EditorUtils.getTransactionalEditingDomain(), Collections.singleton(lowerValue)); copy.execute(); Collection<?> result = copy.getResult(); Object valueToAffect = null; if(!result.isEmpty()) { valueToAffect = result.iterator().next(); } if(valueToAffect instanceof ValueSpecification) { pin.setLowerValue((ValueSpecification)valueToAffect); } } // Synchronize multiplicity : upper value ValueSpecification upperValue = property.getUpperValue(); if(upperValue != null) { // use a copy command for new value Command copy = CopyCommand.create(EditorUtils.getTransactionalEditingDomain(), Collections.singleton(upperValue)); copy.execute(); Collection<?> result = copy.getResult(); Object valueToAffect = null; if(!result.isEmpty()) { valueToAffect = result.iterator().next(); } if(valueToAffect instanceof ValueSpecification) { pin.setUpperValue((ValueSpecification)valueToAffect); } } } return pin; } /** * Create an input pin with valued copied from the parameter * * @param parameter * the reference parameter * @param preferredPinClass * the EClass to use to create a new pin whenever possible (or * null) */ protected static InputPin createInputPin(Parameter parameter, EClass preferredPinClass) { InputPin pin; if(UMLPackage.eINSTANCE.getValuePin().equals(preferredPinClass)) { pin = UMLFactory.eINSTANCE.createValuePin(); } else if(UMLPackage.eINSTANCE.getActionInputPin().equals(preferredPinClass)) { pin = UMLFactory.eINSTANCE.createActionInputPin(); } else { pin = UMLFactory.eINSTANCE.createInputPin(); } assignUpperBound(pin); // Initialize name pin.setName(parameter.getName()); // Synchronize type pin.setType(parameter.getType()); // Synchronize is ordered pin.setIsOrdered(parameter.isOrdered()); // Synchronize multiplicity : is unique pin.setIsUnique(parameter.isUnique()); // Synchronize multiplicity : lower value ValueSpecification lowerValue = parameter.getLowerValue(); if(lowerValue != null) { // use a copy command for new value Command copy = CopyCommand.create(EditorUtils.getTransactionalEditingDomain(), Collections.singleton(lowerValue)); copy.execute(); Collection<?> result = copy.getResult(); Object valueToAffect = null; if(!result.isEmpty()) { valueToAffect = result.iterator().next(); } if(valueToAffect instanceof ValueSpecification) { pin.setLowerValue((ValueSpecification)valueToAffect); } } // Synchronize multiplicity : upper value ValueSpecification upperValue = parameter.getUpperValue(); if(upperValue != null) { // use a copy command for new value Command copy = CopyCommand.create(EditorUtils.getTransactionalEditingDomain(), Collections.singleton(upperValue)); copy.execute(); Collection<?> result = copy.getResult(); Object valueToAffect = null; if(!result.isEmpty()) { valueToAffect = result.iterator().next(); } if(valueToAffect instanceof ValueSpecification) { pin.setUpperValue((ValueSpecification)valueToAffect); } } return pin; } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (AddStructuralFeatureValueAction) * @return command */ protected CompoundCommand getResetPinsCmd(AddStructuralFeatureValueAction action) { // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); if(action.getValue() == null) { InputPin valuePin = createValuePinInAddStructuralFeatureAction(action); Command cmdValuePin = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getWriteStructuralFeatureAction_Value(), valuePin); globalCmd.append(cmdValuePin); } if(action.getObject() == null) { InputPin objectPin = createObjectPinInStructuralFeatureAction(action); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getStructuralFeatureAction_Object(), objectPin); globalCmd.append(cmd); } if(action.getResult() == null) { OutputPin resultPin = createResultPinInStructuralAction(action); Command cmdResultPin = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getWriteStructuralFeatureAction_Result(), resultPin); globalCmd.append(cmdResultPin); } return globalCmd; } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (ReadVariableAction) * @return command */ // Get the editing domain protected CompoundCommand getResetPinsCmd(ReadVariableAction action) { TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); if(action.getResult() == null) { OutputPin resultPin = createResultPin(action.getVariable()); Command cmdResultPin = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getReadVariableAction_Result(), resultPin); globalCmd.append(cmdResultPin); } return globalCmd; } // /** // * Get the command to reset all pins of the action. // * // * @param action // * action to reinitialize pins (BroadcastSignalAction) // * @return command // */ // protected CompoundCommand getResetPinsCmd(BroadcastSignalAction action) { // // Get the editing domain // TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); // CompoundCommand globalCmd = new CompoundCommand(); // if(action.getSignal() != null) { // if(action.getArguments().isEmpty()) { // EList<Property> properties = action.getSignal().getAllAttributes(); // for(Property argument : properties) { // InputPin argPin = UMLFactory.eINSTANCE.createInputPin(); // assignUpperBound(argPin); // argPin.setName(argument.getName()); // argPin.setType(argument.getType()); // Command cmdArg = AddCommand.create(editingdomain, action, UMLPackage.Literals.INVOCATION_ACTION__ARGUMENT, Arrays.asList(argPin)); // globalCmd.append(cmdArg); // } // } // } // return globalCmd; // } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (AddVariableValueAction) * @return command */ protected CompoundCommand getResetPinsCmd(AddVariableValueAction action) { // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); if(action.getValue() == null) { InputPin valuePin = createValuePinInAddVariableValueAction(action.getVariable()); Command cmdValuePin = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getWriteVariableAction_Value(), valuePin); globalCmd.append(cmdValuePin); } if(action.getInsertAt() == null) { InputPin insertAtPin = UMLFactory.eINSTANCE.createInputPin(); assignUpperBound(insertAtPin); insertAtPin.setName(INSERT_AT_IN_ADD_VARIABLE_VALUE_ACTION); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getAddVariableValueAction_InsertAt(), insertAtPin); globalCmd.append(cmd); } return globalCmd; } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (AddStructuralFeatureValueAction) * @return command */ public static CompoundCommand getResetPinsCmd(DestroyObjectAction action) { // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); if(action.getTarget() == null) { InputPin targetPin = UMLFactory.eINSTANCE.createInputPin(); assignUpperBound(targetPin); targetPin.setName(TARGET_IN_DESTROY_OBJECT_ACTION); Command cmdTargetPin = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getDestroyObjectAction_Target(), targetPin); globalCmd.append(cmdTargetPin); } return globalCmd; } /** * Create a Pin value for a Structural feature action * * @param action * @return */ public static InputPin createValuePinInAddStructuralFeatureAction(StructuralFeatureAction action) { InputPin pin = UMLFactory.eINSTANCE.createInputPin(); assignUpperBound(pin); if(action != null) { StructuralFeature feature = action.getStructuralFeature(); if(feature != null && feature.getType() != null) { Type owningType = feature.getType(); if(owningType instanceof Type) { pin.setType((Type)owningType); } } } pin.setName(VALUE_PIN_IN_STRUCTURAL_FEATURE_VALUE_ACTION); return pin; } /** * Create a Pin value for a Structural feature action * * @param action * @return */ protected InputPin createValuePinInAddVariableValueAction(Variable var) { InputPin pin = UMLFactory.eINSTANCE.createInputPin(); assignUpperBound(pin); if(var != null) { Type owningType = var.getType(); if(owningType instanceof Type) { pin.setType((Type)owningType); } } pin.setName(VALUE_IN_ADD_VARIABLE_VALUE_ACTION); return pin; } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (SendObjectAction) * @return command */ public static CompoundCommand getResetPinsCmd(SendObjectAction action) { // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); // add target pin if(action.getTarget() == null) { InputPin targetPin = createTargetPin(null); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getSendObjectAction_Target(), targetPin); globalCmd.append(cmd); } // add request pin if(action.getRequest() == null) { InputPin requestPin = createRequestPin(); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getSendObjectAction_Request(), requestPin); globalCmd.append(cmd); } return globalCmd; } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (ReadStructuralFeatureAction) * @return command */ protected CompoundCommand getResetPinsCmd(ReadStructuralFeatureAction action) { // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); // add result pin if(action.getResult() == null) { OutputPin resultPin = createResultPinInStructuralAction(action); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getReadStructuralFeatureAction_Result(), resultPin); globalCmd.append(cmd); } // add object pin if(action.getObject() == null) { InputPin objectPin = createObjectPinInStructuralFeatureAction(action); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getStructuralFeatureAction_Object(), objectPin); globalCmd.append(cmd); } return globalCmd; } /** * Create the object pin of an ReadStructuralAction * * @param action * @return */ public static InputPin createObjectPinInStructuralFeatureAction(StructuralFeatureAction action) { InputPin pin = UMLFactory.eINSTANCE.createInputPin(); assignUpperBound(pin); if(action != null) { Type type = getTypeFromStructuralFeature(action); if(type != null) { pin.setType(type); } } pin.setName(OBJECT_PIN_IN_READS_STRUCTURAL_ACTION); return pin; } public static Type getTypeFromStructuralFeature(StructuralFeatureAction action) { Type type = null; StructuralFeature feature = action.getStructuralFeature(); if(feature != null) { Element owner = feature.getOwner(); if(owner != null) { if(feature.getFeaturingClassifiers().contains(owner)) { type = ((Type)owner); } } } return type; } /** * Create a simple output pin for a ReadStructura feature FIXME set type * * @param action * @return */ public static OutputPin createResultPinInStructuralAction(StructuralFeatureAction action) { OutputPin pin = UMLFactory.eINSTANCE.createOutputPin(); assignUpperBound(pin); Type type = getTypeFromStructuralFeature(action); if(type != null) { pin.setType(type); } pin.setName(RESULT_PIN_READ_SRTUCTURAL_ACTION); return pin; } private static void assignUpperBound(ObjectNode node) { LiteralInteger literal = UMLFactory.eINSTANCE.createLiteralInteger(); literal.setValue(1); node.setUpperBound(literal); } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (SendObjectAction) * @return command */ protected CompoundCommand getResetPinsCmd(CreateObjectAction action) { // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); // add target pin if(action.getResult() == null) { OutputPin resultPin = createResultPin((Classifier)null); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getCreateObjectAction_Result(), resultPin); globalCmd.append(cmd); } return globalCmd; } // /** // * Get the command to reset all pins of the action. // * // * @param action // * action to reinitialize pins (SendSignalAction) // * @return command // */ // public static CompoundCommand getResetPinsCmd(InvocationAction action) { // if(!(action instanceof SendSignalAction || action instanceof BroadcastSignalAction)) { // return null; // } // // Get the editing domain // TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); // CompoundCommand globalCmd = new CompoundCommand(); // // remove argument pins // if(!action.getArguments().isEmpty()) { // Command cmd = RemoveCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getInvocationAction_Argument(), action.getArguments()); // globalCmd.append(cmd); // } // // recover attributes // List<Property> attributes = Collections.emptyList(); // Signal signal = action instanceof SendSignalAction ? ((SendSignalAction)action).getSignal() : ((BroadcastSignalAction)action).getSignal(); // if(signal != null) { // attributes = signal.getOwnedAttributes(); // } // // add pins corresponding to attributes // Map<Integer, Property> inParameters = new HashMap<Integer, Property>(); // int inIndex = 0; // for(Property att : attributes) { // inParameters.put(inIndex, att); // inIndex++; // } // if(!inParameters.isEmpty()) { // Command cmd = getAddPinsCmd(action, inParameters, null); // globalCmd.append(cmd); // } // if(action instanceof SendSignalAction) { // SendSignalAction sendSignalAction = (SendSignalAction)action; // // add target pin // if(sendSignalAction.getTarget() == null) { // InputPin targetPin = createTargetPin(null); // Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getSendSignalAction_Target(), targetPin); // globalCmd.append(cmd); // } // } // return globalCmd; // } /** * Retrieve the parameter linked * * @param p * Pin where the EAnnotaion is stored * @param xmiResource * @return */ public static TypedElement getLinkedParemeter(Pin p, XMIResource xmiResource) { if(p != null && xmiResource != null) { EAnnotation eAnnotation = p.getEAnnotation(IPinToParameterLinkCommand.PIN_TO_PARAMETER_LINK); if(eAnnotation != null && !eAnnotation.getDetails().isEmpty()) { String id = eAnnotation.getDetails().get(0).getValue(); EObject pa = xmiResource.getEObject(id); if(pa instanceof TypedElement) { return (TypedElement)pa; } } } return null; } /** * Get the command to reset all pins of the action. * * @param action * action to reinitialize pins (CallOperationAction or * CallBehaviorAction) * @return command */ public static CompoundCommand getResetPinsCmd(InvocationAction action) { // Getting the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); CompoundCommand globalCmd = new CompoundCommand(); Element behaviorStructural = null; List<? extends TypedElement> parameters = Collections.emptyList(); if(action instanceof CallBehaviorAction) { behaviorStructural = ((CallBehaviorAction)action).getBehavior(); parameters = ((Behavior)behaviorStructural).getOwnedParameters(); } else if(action instanceof CallOperationAction) { behaviorStructural = ((CallOperationAction)action).getOperation(); parameters = ((Operation)behaviorStructural).getOwnedParameters(); } else if(action instanceof SendSignalAction) { behaviorStructural = ((SendSignalAction)action).getSignal(); parameters = ((Signal)behaviorStructural).getOwnedAttributes(); } else if(action instanceof BroadcastSignalAction) { Signal signal = ((BroadcastSignalAction)action).getSignal(); if(signal != null) { behaviorStructural = signal; parameters = ((Signal)behaviorStructural).getOwnedAttributes(); } else { parameters = Collections.emptyList(); } } XMIResource xmiResource = getXMIResource(behaviorStructural); // Removing input pins that are not up to date. Collection<TypedElement> parameterWhichPinNotDeleted = new ArrayList<TypedElement>(); Iterable<? extends Pin> allPins = Lists.newArrayList(action.getArguments()); if(action instanceof CallAction) { allPins = Iterables.concat(allPins, ((CallAction)action).getResults()); } List<Command> removesCommand = Lists.newArrayList(); for(Pin pin : allPins) { if(SynchronizePinsParametersHandler.isUpToDate(pin, xmiResource)) { TypedElement pa = getLinkedParemeter(pin, xmiResource); parameterWhichPinNotDeleted.add(pa); } else { EReference feature = null; if(pin instanceof InputPin) { feature = UMLPackage.eINSTANCE.getInvocationAction_Argument(); } else if(pin instanceof OutputPin) { feature = UMLPackage.eINSTANCE.getCallAction_Result(); } //Removing the pin. Command cmd = RemoveCommand.create(editingdomain, action, feature, pin); if(cmd.canExecute()) { removesCommand.add(cmd); } } } //Splitting parameters Map<Integer, TypedElement> inParams = new HashMap<Integer, TypedElement>(); Map<Integer, TypedElement> outParams = new HashMap<Integer, TypedElement>(); splitParameters(parameters, parameterWhichPinNotDeleted, inParams, outParams, action); //Creating new pins. if(!inParams.isEmpty() || !outParams.isEmpty()) { Command cmd = getAddPinsCmd(action, inParams, outParams, null); globalCmd.append(cmd); } /* * Append remove command after create command since create command calculate index of new pins before removing those pins */ for(Command rmComand : removesCommand) { globalCmd.append(rmComand); } /* * No need to reset this pin */ if(action instanceof CallOperationAction) { // add target pin Operation operation = ((CallOperationAction)action).getOperation(); if(operation != null) { InputPin targetPin = createTargetPin(operation); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getCallOperationAction_Target(), targetPin); globalCmd.append(cmd); } } else if(action instanceof SendSignalAction) { SendSignalAction sendSignalAction = (SendSignalAction)action; // add target pin if(sendSignalAction.getTarget() == null) { InputPin targetPin = createTargetPin(null); Command cmd = SetCommand.create(editingdomain, action, UMLPackage.eINSTANCE.getSendSignalAction_Target(), targetPin); globalCmd.append(cmd); } } return globalCmd; } /** * Split a list of parameters in two lists : in and out parameters. If a parameter is to be ignored, then "null" is added to the * corresponding list instead. */ public static void splitParameters(List<? extends TypedElement> allParams, Collection<? extends TypedElement> paramsToIgnore, Map<Integer, TypedElement> inParams, Map<Integer, TypedElement> outParams, Action action) { if(action instanceof CallAction) { Integer inIndex = 0; Integer outIndex = 0; for(TypedElement typeElem : allParams) { if(typeElem instanceof Parameter) { Parameter param = (Parameter)typeElem; ParameterDirectionKind direction = param.getDirection(); //In if(direction == ParameterDirectionKind.IN_LITERAL || direction == ParameterDirectionKind.INOUT_LITERAL) { if(!paramsToIgnore.contains(param)) { inParams.put(inIndex, (TypedElement)param); } inIndex++; } //Out if(direction == ParameterDirectionKind.OUT_LITERAL || direction == ParameterDirectionKind.INOUT_LITERAL || direction == ParameterDirectionKind.RETURN_LITERAL) { if(!paramsToIgnore.contains(param)) { outParams.put(outIndex, (TypedElement)param); } outIndex++; } } } } else if(action instanceof InvocationAction) { Integer inIndex = 0; for(TypedElement typeElem : allParams) { if(!paramsToIgnore.contains(typeElem)) { inParams.put(inIndex, (TypedElement)typeElem); } inIndex++; } } } /** * Retrieves the XMIResource * * @param behaviorStructural * @return */ public static XMIResource getXMIResource(Element behaviorStructural) { XMIResource xmiResource = null; if(behaviorStructural != null) { Resource resource = behaviorStructural.eResource(); if(resource instanceof XMIResource) { xmiResource = (XMIResource)resource; } } return xmiResource; } /** * Get the command to update a pins list with the name if not set yet * * @param pins * the list of pins to update * @param name * the new name set on parameter * @return the command to execute */ protected CompoundCommand getSetPinsNamesCmd(List<Pin> pins, String name) { CompoundCommand globalCmd = new CompoundCommand(); if(pins == null || name == null || "".equals(name)) { return globalCmd; } // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); for(Pin pin : pins) { // erase the name only if null (not set, the user may have set an // empty string name) if(pin.getName() == null) { // add the command Command cmd = SetCommand.create(editingdomain, pin, UMLPackage.eINSTANCE.getNamedElement_Name(), name); globalCmd.append(cmd); } } return globalCmd; } /** * Get the command to update a pins list with given values * * @param pins * the list of pins to update * @param type * the new type value * @param ordered * the new isOrdered value * @param unique * the new isUnique value * @param lower * the new lower value * @param lowerValue * the new lowerValue value * @param upper * the new upper value * @param upperValue * the new upperValue value * @return the command to execute */ protected CompoundCommand getUpdatePinsCmd(List<Pin> pins, Type type, boolean ordered, boolean unique, int lower, ValueSpecification lowerValue, int upper, ValueSpecification upperValue) { CompoundCommand globalCmd = new CompoundCommand(); if(pins == null) { return globalCmd; } // Get the editing domain TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); for(Pin pin : pins) { if(!EcoreUtil.equals(pin.getType(), type)) { // add the command Command cmd = SetCommand.create(editingdomain, pin, UMLPackage.eINSTANCE.getTypedElement_Type(), type); globalCmd.append(cmd); } if(pin.isOrdered() != ordered) { // add the command Command cmd = SetCommand.create(editingdomain, pin, UMLPackage.eINSTANCE.getMultiplicityElement_IsOrdered(), ordered); globalCmd.append(cmd); } if(pin.isUnique() != unique) { // add the command Command cmd = SetCommand.create(editingdomain, pin, UMLPackage.eINSTANCE.getMultiplicityElement_IsUnique(), unique); globalCmd.append(cmd); } // UMLPackage.eINSTANCE.getMultiplicityElement_Lower() is derived // from // UMLPackage.eINSTANCE.getMultiplicityElement_LowerValue() if(!EcoreUtil.equals(pin.getLowerValue(), lowerValue)) { Object affectedvalue = null; // Execute a copy command then add the set command if(lowerValue != null) { Command copy = CopyCommand.create(editingdomain, Collections.singleton(lowerValue)); copy.execute(); Collection<?> result = copy.getResult(); if(!result.isEmpty()) { affectedvalue = result.iterator().next(); } } Command cmd = SetCommand.create(editingdomain, pin, UMLPackage.eINSTANCE.getMultiplicityElement_LowerValue(), affectedvalue); globalCmd.append(cmd); } // UMLPackage.eINSTANCE.getMultiplicityElement_Upper() is derived // from // UMLPackage.eINSTANCE.getMultiplicityElement_UpperValue() if(!EcoreUtil.equals(pin.getUpperValue(), upperValue)) { Object affectedvalue = null; // Execute a copy command then add the set command if(upperValue != null) { Command copy = CopyCommand.create(editingdomain, Collections.singleton(upperValue)); copy.execute(); Collection<?> result = copy.getResult(); if(!result.isEmpty()) { affectedvalue = result.iterator().next(); } } Command cmd = SetCommand.create(editingdomain, pin, UMLPackage.eINSTANCE.getMultiplicityElement_UpperValue(), affectedvalue); globalCmd.append(cmd); } } return globalCmd; } /** * Get the object invoked by the pin's parent action * * @return invoked operation, invoked behavior or null */ static protected NamedElement getInvokedObject(Pin pin) { Element action = pin.getOwner(); if(action instanceof CallOperationAction) { Operation operation = ((CallOperationAction)action).getOperation(); return operation; } else if(action instanceof CallBehaviorAction) { Behavior behavior = ((CallBehaviorAction)action).getBehavior(); return behavior; } else if(action instanceof SendSignalAction) { Signal signal = ((SendSignalAction)action).getSignal(); return signal; } return null; } /** * Get all Pins associated to the property (provided no pin or property has * been added without synchronization) * * @param property * the property * @return the list of associated pins */ static protected List<Pin> getPins(Property property) { Element owner = property.getOwner(); if(owner instanceof Signal) { // initialize listOfPins List<Pin> listOfPins = new LinkedList<Pin>(); // get index of pins int inIndex = ((Signal)owner).getAttributes().indexOf(property); List<InvocationAction> callingActions = getCallingActions(owner); // inspect each referencing action for(InvocationAction action : callingActions) { // owner is action's sent Signal Pin pin = ((SendSignalAction)action).getArguments().get(inIndex); if(pin != null) { listOfPins.add(pin); } } return listOfPins; } return Collections.emptyList(); } /** * Get all Pins associated to the parameter (provided no pin or parameter * has been added without synchronization) * * @param parameter * the parameter * @return the list of associated pins */ static protected List<Pin> getPins(Parameter parameter) { Element owner = parameter.getOwner(); List<InvocationAction> callingActions = getCallingActions(owner); // initialize listOfPins List<Pin> listOfPins = new LinkedList<Pin>(); // get index of pins int inIndex = -1; int outIndex = -1; switch(parameter.getDirection()) { case IN_LITERAL: inIndex = getIndex(parameter, true); break; case OUT_LITERAL: case RETURN_LITERAL: outIndex = getIndex(parameter, false); break; case INOUT_LITERAL: inIndex = getIndex(parameter, true); outIndex = getIndex(parameter, false); break; } if(owner instanceof Operation) { // inspect each referencing action for(InvocationAction action : callingActions) { // owner is action's called Operation switch(parameter.getDirection()) { case IN_LITERAL: Pin pin = ((CallOperationAction)action).getArguments().get(inIndex); if(pin != null) { listOfPins.add(pin); } break; case OUT_LITERAL: case RETURN_LITERAL: pin = ((CallOperationAction)action).getResults().get(outIndex); if(pin != null) { listOfPins.add(pin); } break; case INOUT_LITERAL: pin = ((CallOperationAction)action).getArguments().get(inIndex); if(pin != null) { listOfPins.add(pin); } pin = ((CallOperationAction)action).getResults().get(outIndex); if(pin != null) { listOfPins.add(pin); } break; } } return listOfPins; } else if(owner instanceof Behavior) { // inspect each referencing action for(InvocationAction action : callingActions) { // owner is action's called Behavior switch(parameter.getDirection()) { case IN_LITERAL: Pin pin = ((CallBehaviorAction)action).getArguments().get(inIndex); if(pin != null) { listOfPins.add(pin); } break; case OUT_LITERAL: case RETURN_LITERAL: pin = ((CallBehaviorAction)action).getResults().get(outIndex); if(pin != null) { listOfPins.add(pin); } break; case INOUT_LITERAL: pin = ((CallBehaviorAction)action).getArguments().get(inIndex); if(pin != null) { listOfPins.add(pin); } pin = ((CallBehaviorAction)action).getResults().get(outIndex); if(pin != null) { listOfPins.add(pin); } break; } } return listOfPins; } return Collections.emptyList(); } /** * Get the index (considering Parameters in or out parameters only) at which * the parameter appears in its container. For convenience with Pin mapping, * in-out parameters counted are in both solution. Note that this count does * not take in account the searched for parameter direction. This means that * this method works even if the searched parameter has a different * direction than the one specified in in (usefull when direction changes). * * @param typedElement * the searched parameter * @param in * if true, compute position in Parameters of direction in, if * false, of direction out * @return the position in which the parameter appears (0 based) or -1 if * failed */ static protected int getIndex(TypedElement typedElement, boolean in) { if(typedElement == null) { return -1; } Element owner = typedElement.getOwner(); List<Parameter> parametersList = Collections.emptyList(); if(owner instanceof Operation) { parametersList = ((Operation)owner).getOwnedParameters(); } else if(owner instanceof Behavior) { parametersList = ((Behavior)owner).getOwnedParameters(); } int index = 0; for(Parameter param : parametersList) { if(param.equals(typedElement)) { return index; } if(in && (ParameterDirectionKind.IN_LITERAL.equals(param.getDirection()) || ParameterDirectionKind.INOUT_LITERAL.equals(param.getDirection()))) { index++; } else if(!in && (ParameterDirectionKind.OUT_LITERAL.equals(param.getDirection()) || ParameterDirectionKind.RETURN_LITERAL.equals(param.getDirection()) || ParameterDirectionKind.INOUT_LITERAL.equals(param.getDirection()))) { index++; } } return -1; } /** * Ask the user to validate all the implied modifications (parameters and * all associated pins) * * @param listOfActions * the list of impacted calling actions * @return whether the user validates the modifications */ protected boolean askForValidation(final List<? extends NamedElement> listOfActions) { SafeDialogOpenerDuringValidation<Boolean> opener = new SafeDialogOpenerDuringValidation<Boolean>() { protected Boolean openDialog() { return ConfirmPinAndParameterSyncDialog.openConfirmFromParameter(Display.getDefault().getActiveShell(), listOfActions, labelProvider); } }; return opener.execute(); } }