diff options
Diffstat (limited to 'extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core')
38 files changed, 4687 insertions, 0 deletions
diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/Activator.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/Activator.java new file mode 100644 index 00000000000..8859e978d40 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/Activator.java @@ -0,0 +1,60 @@ +package org.eclipse.papyrus.qompass.modellibs.core; + +import org.eclipse.papyrus.infra.core.log.LogHelper; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.papyrus.qompass.modellibs.core"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + public static LogHelper log; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + + // register the login helper + log = new LogHelper(plugin); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/HelloWorldModelWizard.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/HelloWorldModelWizard.java new file mode 100644 index 00000000000..5972200b5ff --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/HelloWorldModelWizard.java @@ -0,0 +1,27 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core; + +import org.eclipse.papyrus.uml.diagram.wizards.ModelCopyWizard; + +/** + * Copy wizard for the HelloWorld example + */ +public class HelloWorldModelWizard extends ModelCopyWizard { + + public HelloWorldModelWizard() { + super("HelloWorld"); //$NON-NLS-1$ + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/ProducerConsumerModelWizard.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/ProducerConsumerModelWizard.java new file mode 100644 index 00000000000..38ae39a4204 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/ProducerConsumerModelWizard.java @@ -0,0 +1,27 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core; + +import org.eclipse.papyrus.uml.diagram.wizards.ModelCopyWizard; + +/** + * Copy wizard for the ProducerConsumer example + */ +public class ProducerConsumerModelWizard extends ModelCopyWizard { + + public ProducerConsumerModelWizard() { + super("ProducerConsumer"); //$NON-NLS-1$ + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/BindCppIncludeToFirstActual.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/BindCppIncludeToFirstActual.java new file mode 100644 index 00000000000..a8e25cbbf12 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/BindCppIncludeToFirstActual.java @@ -0,0 +1,73 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.bindinghelpers; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.papyrus.C_Cpp.Include; +import org.eclipse.papyrus.FCM.util.IBindingHelper; +import org.eclipse.papyrus.qompass.designer.core.listeners.PostCopyListener; +import org.eclipse.papyrus.qompass.designer.core.templates.TemplateUtils; +import org.eclipse.papyrus.qompass.designer.core.templates.TextTemplateBinding; +import org.eclipse.papyrus.qompass.designer.core.transformations.LazyCopier; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationContext; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationException; +import org.eclipse.uml2.uml.Classifier; +import org.eclipse.uml2.uml.Element; +import org.eclipse.uml2.uml.TemplateBinding; +import org.eclipse.uml2.uml.util.UMLUtil; + +/** + * Instantiate (bind Acceleo template) the text within a C++Include stereotype. + * + * The actual is the first actual within the template binding. This function does not check + * whether the classifier has the template stereotype. + * + * Note: this function is C++ specific, but many parts of the model library are C++ specific as well + * + */ +public class BindCppIncludeToFirstActual implements PostCopyListener, IBindingHelper { + + @Override + public void postCopyEObject(LazyCopier copy, EObject targetEObj) { + // if (copy.get(sourceEObj) isWithinTemplate) + if (targetEObj instanceof Classifier) { + + Classifier targetCl = (Classifier) targetEObj; + try { + Classifier actual = TemplateUtils.getFirstActualFromBinding(binding); + Include cppInclude = UMLUtil.getStereotypeApplication(targetCl, Include.class); + if ((actual != null) && (cppInclude != null)) { + TransformationContext.classifier = targetCl; + String newBody = TextTemplateBinding.bind(cppInclude.getBody(), actual, null); + String newPreBody = TextTemplateBinding.bind(cppInclude.getPreBody(), actual, null); + String newHeader = TextTemplateBinding.bind(cppInclude.getHeader(), actual, null); + cppInclude.setBody(newBody); + cppInclude.setPreBody(newPreBody); + cppInclude.setHeader(newHeader); + } + } catch (TransformationException e) { + // create nested exception + throw new RuntimeException(e); + } + } + } + + protected TemplateBinding binding; + + @Override + public void handleElement(TemplateBinding binding, Element object) { + this.binding = binding; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/BindOperation.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/BindOperation.java new file mode 100644 index 00000000000..6b2d352e1ee --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/BindOperation.java @@ -0,0 +1,54 @@ +package org.eclipse.papyrus.qompass.modellibs.core.bindinghelpers; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.papyrus.FCM.util.IBindingHelper; +import org.eclipse.papyrus.qompass.designer.core.listeners.PreCopyListener; +import org.eclipse.papyrus.qompass.designer.core.templates.BindingUtils; +import org.eclipse.papyrus.qompass.designer.core.templates.TemplateUtils; +import org.eclipse.papyrus.qompass.designer.core.transformations.LazyCopier; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationException; +import org.eclipse.papyrus.qompass.modellibs.core.Activator; +import org.eclipse.uml2.uml.Behavior; +import org.eclipse.uml2.uml.Classifier; +import org.eclipse.uml2.uml.Element; +import org.eclipse.uml2.uml.OpaqueBehavior; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.TemplateBinding; + +/** + * Bind an operation to an actual, i.e. evaluate the Acceleo template within the opaque behavior associated with + * the operation. + */ +public class BindOperation implements IBindingHelper, PreCopyListener { + + private TemplateBinding binding; + + @Override + public EObject preCopyEObject(LazyCopier copy, EObject sourceEObj) { + + if (sourceEObj instanceof Operation) { + Operation operation = (Operation) sourceEObj; + Classifier actual = TemplateUtils.getFirstActualFromBinding(binding); + + Operation newOperation = BindingUtils.instantiateOperation(copy, actual, operation); + for (Behavior method : operation.getMethods()) { + if (method instanceof OpaqueBehavior) { + try { + Behavior newBehavior = + BindingUtils.instantiateBehavior(copy, actual, (OpaqueBehavior) method); + newBehavior.setSpecification(newOperation); + } catch (TransformationException e) { + Activator.log.error(e); + } + } + } + return newOperation; + } + return sourceEObj; + } + + @Override + public void handleElement(TemplateBinding binding, Element object) { + this.binding = binding; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/InstantiateCppIncludeWithItSelf.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/InstantiateCppIncludeWithItSelf.java new file mode 100644 index 00000000000..cd4a730ee30 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/InstantiateCppIncludeWithItSelf.java @@ -0,0 +1,74 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.bindinghelpers; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.papyrus.C_Cpp.Include; +import org.eclipse.papyrus.FCM.Template; +import org.eclipse.papyrus.FCM.util.IBindingHelper; +import org.eclipse.papyrus.qompass.designer.core.listeners.PostCopyListener; +import org.eclipse.papyrus.qompass.designer.core.templates.TextTemplateBinding; +import org.eclipse.papyrus.qompass.designer.core.transformations.LazyCopier; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationContext; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationException; +import org.eclipse.uml2.uml.Classifier; +import org.eclipse.uml2.uml.Element; +import org.eclipse.uml2.uml.TemplateBinding; +import org.eclipse.uml2.uml.util.UMLUtil; + +/** + * Instantiate (bind Acceleo template) the text within a C++Include stereotype. + * + * Difference to InstantiateCppInclude: This function does not obtain the actual from a template + * binding. Instead it takes the classifier having the Template stereotype as actual. + * TODO: What's the use of this function. The classifier is known at this time - unless in a template? + * + * Note: this function is C++ specific, but many parts of the model library are C++ specific as well + * + */ +@Deprecated +public class InstantiateCppIncludeWithItSelf implements PostCopyListener, IBindingHelper { + + @Override + public void postCopyEObject(LazyCopier copy, EObject targetEObj) { + // if (copy.get(sourceEObj) isWithinTemplate) + if (targetEObj instanceof Classifier) { + // TODO: C++ specific code! + Classifier targetCl = (Classifier) targetEObj; + Template template = UMLUtil.getStereotypeApplication(targetCl, Template.class); + // apply, in case of pass-classifier + if ((template != null) && (template.getHelper() == null)) { + try { + Include cppInclude = UMLUtil.getStereotypeApplication(targetCl, Include.class); + TransformationContext.classifier = targetCl; + String newBody = TextTemplateBinding.bind(cppInclude.getBody(), targetCl, null); + String newPreBody = TextTemplateBinding.bind(cppInclude.getPreBody(), targetCl, null); + String newHeader = TextTemplateBinding.bind(cppInclude.getHeader(), targetCl, null); + cppInclude.setBody(newBody); + cppInclude.setPreBody(newPreBody); + cppInclude.setHeader(newHeader); + } catch (TransformationException e) { + // create nested exception + throw new RuntimeException(e); + } + } + } + } + + @Override + public void handleElement(TemplateBinding binding, Element object) { + // don't need to handle binding + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/LoopOperations.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/LoopOperations.java new file mode 100644 index 00000000000..996363af3dd --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/bindinghelpers/LoopOperations.java @@ -0,0 +1,130 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.bindinghelpers; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.papyrus.FCM.util.IBindingHelper; +import org.eclipse.papyrus.qompass.designer.core.listeners.PreCopyListener; +import org.eclipse.papyrus.qompass.designer.core.templates.BindingUtils; +import org.eclipse.papyrus.qompass.designer.core.templates.TemplateUtils; +import org.eclipse.papyrus.qompass.designer.core.templates.TextTemplateBinding; +import org.eclipse.papyrus.qompass.designer.core.transformations.LazyCopier; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationException; +import org.eclipse.papyrus.qompass.modellibs.core.Activator; +import org.eclipse.uml2.uml.Behavior; +import org.eclipse.uml2.uml.Classifier; +import org.eclipse.uml2.uml.Element; +import org.eclipse.uml2.uml.EnumerationLiteral; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.OpaqueBehavior; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.TemplateBinding; +import org.eclipse.uml2.uml.Type; + +/** + * This binding helper loops over all operations of the actual template parameter + * (typically an interface) + * + */ +public class LoopOperations implements IBindingHelper, PreCopyListener { + + private TemplateBinding binding; + + @Override + public EObject preCopyEObject(LazyCopier copy, EObject sourceEObj) { + + if (sourceEObj instanceof Operation) { + Operation operation = (Operation) sourceEObj; + + Classifier actual = TemplateUtils.getFirstActualFromBinding(binding); + + if (!(actual instanceof Interface)) { + return sourceEObj; + } + Interface passedActualIntf = (Interface) actual; + Operation last = null; + EList<Element> removalList = new BasicEList<Element>(); + for (Operation intfOperation : passedActualIntf.getAllOperations()) { + for (Element removalElement : removalList) { + copy.removeForCopy(removalElement); // enable subsequent instantiations + } + removalList.clear(); + last = BindingUtils.instantiateOperation(copy, intfOperation, operation); + removalList.add(operation); + for (Behavior method : operation.getMethods()) { + if (method instanceof OpaqueBehavior) { + try { + Behavior newBehavior = + BindingUtils.instantiateBehavior(copy, intfOperation, (OpaqueBehavior) method); + newBehavior.setSpecification(last); + } catch (TransformationException e) { + Activator.log.error(e); + ; + } + // removalList.add(method); + copy.removeForCopy(method); // enable subsequent instantiations + } + } + } + // from a logical viewpoint, we need to copy parameters & name, but not the + // operation identity. + copy.put(operation, last); + return last; + /* + * else { // not LOOP_OPERATIONS + * Operation newOperation = instantiateOperation(actual, template, operation, boundClass); + * for(Behavior method : operation.getMethods()) { + * if(method instanceof OpaqueBehavior) { + * Behavior newBehavior = + * instantiateBehavior(actual, template, (OpaqueBehavior)method); + * newBehavior.setSpecification(newOperation); + * } + * } + * return newOperation; + */ + } + else if (sourceEObj instanceof EnumerationLiteral) { + EnumerationLiteral literal = (EnumerationLiteral) sourceEObj; + Classifier actual = TemplateUtils.getFirstActualFromBinding(binding); + // Type passedActual = getPassedActual(template, actual, boundClass); + Type passedActual = actual; + if (!(passedActual instanceof Interface)) { + return sourceEObj; + } + Interface passedActualIntf = (Interface) passedActual; + EnumerationLiteral newLiteral = null; + for (Operation intfOperation : passedActualIntf.getAllOperations()) { + copy.removeForCopy(literal); + newLiteral = copy.getCopy(literal); + try { + String newName = TextTemplateBinding.bind(literal.getName(), intfOperation, null); + newLiteral.setName(newName); + } catch (TransformationException e) { + Activator.log.error(e); + newLiteral.setName("none"); //$NON-NLS-1$ + } + } + return newLiteral; + } + return null; + } + + @Override + public void handleElement(TemplateBinding binding, Element object) { + this.binding = binding; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/embeddingrules/AccordCall.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/embeddingrules/AccordCall.java new file mode 100644 index 00000000000..e76e284f645 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/embeddingrules/AccordCall.java @@ -0,0 +1,95 @@ +/*****************************************************************************
+ * Copyright (c) 2013 CEA LIST.
+ *
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ansgar Radermacher ansgar.radermacher@cea.fr
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.qompass.modellibs.core.embeddingrules;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.papyrus.FCM.Connector;
+import org.eclipse.papyrus.FCM.util.ConnectorTypeUtil;
+import org.eclipse.papyrus.FCM.util.FCMUtil;
+import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil;
+import org.eclipse.uml2.uml.ConnectableElement;
+import org.eclipse.uml2.uml.NamedElement;
+import org.eclipse.uml2.uml.util.UMLUtil;
+
+/**
+ * Embedding rule
+ * TODO: currently unused
+ *
+ */
+// @unused
+public class AccordCall extends ConnectorTypeUtil {
+
+ private ConnectableElement clientRole = null;
+ private ConnectableElement serverRole = null;
+ private ConnectableElement rtuRole = null;
+ private ConnectableElement connectorRole = null;
+
+ @Override
+ public FCMUtil.RoleBindingTable getRoleBindings(Connector connector) {
+ super.getRoleBindings(connector);
+
+ clientRole = bindingTable.getRoleKeyByName("client");
+ serverRole = bindingTable.getRoleKeyByName("server");
+ rtuRole = bindingTable.getRoleKeyByName("rtu");
+ connectorRole = bindingTable.getRoleKeyByName("connector");
+
+ for (org.eclipse.uml2.uml.ConnectorEnd end : connector.getBase_Connector().getEnds()) {
+ if (end.getRole() instanceof org.eclipse.uml2.uml.Port) {
+ org.eclipse.uml2.uml.Port port = (org.eclipse.uml2.uml.Port) end.getRole();
+ org.eclipse.uml2.uml.Property part = end.getPartWithPort();
+ if (StereotypeUtil.isApplied(port, org.eclipse.papyrus.FCM.Port.class)) {
+ org.eclipse.papyrus.FCM.Port fcmPort = UMLUtil.getStereotypeApplication(port, org.eclipse.papyrus.FCM.Port.class);
+ if (fcmPort.getKind().getBase_Class().getName().equals("UseInterfaceWithRtf")) {
+ // => elements associated with the connector end play the client role
+ List<NamedElement> clientActors = new ArrayList<NamedElement>();
+ clientActors.add(port);
+ clientActors.add(part);
+ bindingTable.addEntry(clientRole, clientActors);
+ }
+ else if (fcmPort.getKind().getBase_Class().getName().equals("ProvideInterface")) {
+ // => elements associated with the connector end play the server role
+ List<NamedElement> serverActors = new ArrayList<NamedElement>();
+ serverActors.add(port);
+ serverActors.add(part);
+ bindingTable.addEntry(serverRole, serverActors);
+ // the property playing the server role must also play the rtu role
+ port = ((org.eclipse.uml2.uml.Class) part.getType()).getOwnedPort("rtu", null);
+ if (port == null) {
+ if (((org.eclipse.uml2.uml.Class) part.getType()).getInheritedMember("rtu") != null &&
+ ((org.eclipse.uml2.uml.Class) part.getType()).getInheritedMember("rtu") instanceof org.eclipse.uml2.uml.Port) {
+ port = (org.eclipse.uml2.uml.Port) ((org.eclipse.uml2.uml.Class) part.getType()).getInheritedMember("rtu");
+ }
+ else {
+ System.out.println("Could not find a port rtu on part " + part.getName() + " : " + part.getType());
+ }
+ }
+ if (port != null) {
+ List<NamedElement> rtuActors = new ArrayList<NamedElement>();
+ rtuActors.add(port);
+ rtuActors.add(part);
+ bindingTable.addEntry(rtuRole, rtuActors);
+ }
+ }
+ }
+ }
+ }
+ List<NamedElement> connectorActors = new ArrayList<NamedElement>();
+ connectorActors.add(connector.getBase_Connector());
+ bindingTable.addEntry(connectorRole, connectorActors);
+ return bindingTable;
+ }
+}
diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/iconfigurators/AnimServiceConfigurator.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/iconfigurators/AnimServiceConfigurator.java new file mode 100644 index 00000000000..b33d4053ed2 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/iconfigurators/AnimServiceConfigurator.java @@ -0,0 +1,68 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.iconfigurators; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.papyrus.qompass.designer.core.deployment.AllocUtils; +import org.eclipse.papyrus.qompass.designer.core.extensions.IInstanceConfigurator; +import org.eclipse.uml2.uml.InstanceSpecification; +import org.eclipse.uml2.uml.NamedElement; +import org.eclipse.uml2.uml.Property; + +/** + * Configurator for the Eclipse animation server. This instance is systematically + * allocated to a node named "Eclipse" call event (for a state machine): it sets the + * portID attribute of the call event interceptor. The interceptor uses this + * attribute to initialize the portID attribute within the produced CallEvent + * data structure. + * + * @author ansgar + * + */ +public class AnimServiceConfigurator implements IInstanceConfigurator { + + public final static String eclipseAnimService = "Eclipse"; //$NON-NLS-1$ + + /** + * Configure the instance of a CallEvent interceptor. The configuration parameter is the + * index of the port which gets intercepted. It is obtained via an enumeration + * + * @see org.eclipse.papyrus.qompass.designer.core.extensions.IInstanceConfigurator#configureInstance(org.eclipse.uml2.uml.InstanceSpecification, org.eclipse.uml2.uml.InstanceSpecification, org.eclipse.uml2.uml.Port) + * + * @param instance + * the instance that should be configured + * @param componentPart + * the part representing this instance + * @param context + * container context + */ + @Override + public void configureInstance(InstanceSpecification instance, Property componentPart, InstanceSpecification parentInstance) + { + EList<InstanceSpecification> nodes = AllocUtils.getAllNodesOrThreadsParent(parentInstance); + if (nodes.size() > 0) { + InstanceSpecification node = nodes.get(0); + // problem: instance specification is within intermediate model, thus incomplete. + // option: explicitly pre-create singletons (and allocate these?) + NamedElement animService = node.getNearestPackage().getMember(eclipseAnimService); + if (animService instanceof InstanceSpecification) { + AllocUtils.allocate(instance, (InstanceSpecification) animService); + return; + } + } + // throw new TransformationRTException(String.format("Cannot find node <%s> in platform definition", eclipseAnimService)); + } + +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/iconfigurators/CallEventConfigurator.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/iconfigurators/CallEventConfigurator.java new file mode 100644 index 00000000000..db9831cd43a --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/iconfigurators/CallEventConfigurator.java @@ -0,0 +1,61 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.iconfigurators; + +import org.eclipse.papyrus.qompass.designer.core.deployment.DepPlanUtils; +import org.eclipse.papyrus.qompass.designer.core.extensions.IInstanceConfigurator; +import org.eclipse.uml2.uml.InstanceSpecification; +import org.eclipse.uml2.uml.Property; + +/** + * Configurator of a call event (for a state machine): it sets the + * portID attribute of the call event interceptor. The interceptor uses this + * attribute to initialize the portID attribute within the produced CallEvent + * data structure. + * + * @author ansgar + * + */ +public class CallEventConfigurator implements IInstanceConfigurator { + + public final static String portAttribute = "portID"; //$NON-NLS-1$ + + /** + * Configure the instance of a CallEvent interceptor. The configuration parameter is the + * index of the port which gets intercepted. It is obtained via an enumeration + * + * @see org.eclipse.papyrus.qompass.designer.core.extensions.IInstanceConfigurator#configureInstance(org.eclipse.uml2.uml.InstanceSpecification, org.eclipse.uml2.uml.InstanceSpecification, org.eclipse.uml2.uml.Port) + * + * @param instance + * the instance that should be configured + * @param componentPart + * the part representing this instance + * @param context + * container context + */ + @Override + public void configureInstance(InstanceSpecification instance, Property componentPart, InstanceSpecification parentInstance) + { + if (parentInstance != null) { + // make sure that there is an enum par port + // String literalName = "port_" + UMLTool.varName(context.port); //$NON-NLS-1$ + + // the associated enumeration is declared by the statemachine (which is included by the bootloader as well) + + DepPlanUtils.configureProperty(instance, portAttribute, 0); + } + } + +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/AMIcallback.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/AMIcallback.java new file mode 100644 index 00000000000..74809d9cc9b --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/AMIcallback.java @@ -0,0 +1,162 @@ +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import java.util.Iterator; + +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Parameter; +import org.eclipse.uml2.uml.ParameterDirectionKind; +import org.eclipse.uml2.uml.Type; + + +public class AMIcallback implements IMappingRule { + + private static final String _REPLY = "_reply_"; //$NON-NLS-1$ + private static final String _REQUEST = "_request_"; //$NON-NLS-1$ + private static final String AMI_CB = "AMI_CB"; //$NON-NLS-1$ + + @Override + public Type calcDerivedType(Port p, boolean update) { + Type type = p.getType(); + if (!(type instanceof Interface)) { + return null; + } + + Interface typingInterface = (Interface) type; + Class derivedType = MapUtil.getDerivedClass(p, AMI_CB); + Interface derivedRequestInterface = MapUtil.getDerivedInterface(p, _REQUEST); + Interface derivedReplyInterface = MapUtil.getDerivedInterface(p, _REPLY); + MapUtil.addUsage(derivedType, derivedRequestInterface); // caller can use (require) the request interface + MapUtil.addRealization(derivedType, derivedReplyInterface); // callers must implement (provide) the reply interface + + if (!update) { + return derivedType; + } + + // ----------------------------------------------- + // calculate "request" interface (OUT parameter are removed) + // ----------------------------------------------- + for (Operation operation : typingInterface.getOwnedOperations()) { + String name = operation.getName(); + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedRequestInterface.getOperation(name, null, null); + if (derivedOperation == null) { + derivedOperation = derivedRequestInterface.createOwnedOperation(name, null, null); + } + + // request operation contains only in and inout parameters + for (Parameter parameter : operation.getOwnedParameters()) { + if ((parameter.getDirection() == ParameterDirectionKind.IN_LITERAL) || + (parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL)) { + + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (derivedOperation.getOwnedParameter(paramName, paramType) == null) { + Parameter newParameter = derivedOperation.createOwnedParameter(parameter.getName(), parameter.getType()); + newParameter.setDirection(parameter.getDirection()); + newParameter.setLower(parameter.getLower()); + newParameter.setUpper(parameter.getUpper()); + } + } + } + + // remove those parameters that exist in derived, but not original interface. + Iterator<Parameter> derivedParameters = derivedOperation.getOwnedParameters().iterator(); + while (derivedParameters.hasNext()) { + Parameter parameter = derivedParameters.next(); + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (operation.getOwnedParameter(paramName, paramType) == null) { + // not on in original interface, remove from derived as well + derivedParameters.remove(); + } + } + } + + // check whether operations in derived interface exist in original interface + // (remove, if not) + Iterator<Operation> derivedRequestOperations = derivedRequestInterface.getOwnedOperations().iterator(); + while (derivedRequestOperations.hasNext()) { + Operation derivedOperation = derivedRequestOperations.next(); + String name = derivedOperation.getName(); + if (name == null) { + continue; + } + if (typingInterface.getOperation(name, null, null) == null) { + // not in typing interface, remove + derivedRequestOperations.remove(); + } + } + + // ----------------------------------------------- + // calculate "reply" interface (with OUT and INOUT parameter transformed into in parameters) + // ----------------------------------------------- + for (Operation operation : typingInterface.getOwnedOperations()) { + String name = operation.getName(); + + if (AMIpoll.hasOutParameters(operation)) { + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedReplyInterface.getOperation(name, null, null); + if (derivedOperation == null) { + derivedOperation = derivedReplyInterface.createOwnedOperation(name, null, null); + } + + // each non-in parameter is in the poll operation. + for (Parameter parameter : operation.getOwnedParameters()) { + if (parameter.getDirection() != ParameterDirectionKind.IN_LITERAL) { // OUT and INOUT + + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (derivedOperation.getOwnedParameter(paramName, paramType) == null) { + Parameter newParameter = derivedOperation.createOwnedParameter(parameter.getName(), parameter.getType()); + newParameter.setDirection(ParameterDirectionKind.IN_LITERAL); + newParameter.setLower(parameter.getLower()); + newParameter.setUpper(parameter.getUpper()); + } + } + } + + // remove those parameters that exist in derived, but not original interface. + Iterator<Parameter> derivedParameters = derivedOperation.getOwnedParameters().iterator(); + while (derivedParameters.hasNext()) { + Parameter parameter = derivedParameters.next(); + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (operation.getOwnedParameter(paramName, paramType) == null) { + // not on in original interface, remove from derived as well + derivedParameters.remove(); + } + } + } + } + + // check whether operations in derived interface exist in original interface + // (remove, if not) + Iterator<Operation> derivedReplyOperations = derivedReplyInterface.getOwnedOperations().iterator(); + while (derivedRequestOperations.hasNext()) { + Operation derivedOperation = derivedReplyOperations.next(); + String name = derivedOperation.getName(); + if (name == null) { + continue; + } + if (typingInterface.getOperation(name, null, null) == null) { + // not in typing interface, remove + derivedReplyOperations.remove(); + } + } + + return derivedType; + } + + @Override + public boolean needsUpdate(Port p) { + // TODO: insufficient condition + return (calcDerivedType(p, false) == null); + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/AMIpoll.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/AMIpoll.java new file mode 100644 index 00000000000..220f2021465 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/AMIpoll.java @@ -0,0 +1,161 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import java.util.Iterator; + +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Parameter; +import org.eclipse.uml2.uml.ParameterDirectionKind; +import org.eclipse.uml2.uml.Type; + + +/** + * Implement a CORBA AMI style polling interface + * This is defined in the CORBA 3 standard in section 22.6.2 + * A difference is that we do not return the poller object, but the client uses the original port. + * This has the consequence that it is not possible to make multiple asynchronous requests. + * + * A second difference is that we do not keep the normal methods in the same interface and prefix the + * new ones. (which would probably be a good idea). + * + */ +public class AMIpoll implements IMappingRule { + + private static final String IAMI_POLL = "IAMIPoll_"; //$NON-NLS-1$ + private static final String AMI_POLL = "AMIPoll_"; //$NON-NLS-1$ + + @Override + public Type calcDerivedType(Port p, boolean update) { + Type type = p.getType(); + if (!(type instanceof Interface)) { + return null; + } + + Class derivedType = MapUtil.getDerivedClass(p, AMI_POLL); + Interface typingInterface = (Interface) type; + Interface derivedInterface = MapUtil.getDerivedInterface(p, IAMI_POLL); + MapUtil.addUsage(derivedType, derivedInterface); + + if (!update) { + return derivedType; + } + + for (Operation operation : typingInterface.getOwnedOperations()) { + String name = operation.getName(); + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedInterface.getOperation(name, null, null); + if (derivedOperation == null) { + derivedOperation = derivedInterface.createOwnedOperation(name, null, null); + } + + if (hasOutParameters(operation)) { + String pollName = name + "Poll"; //$NON-NLS-1$ + Operation derivedPollOperation = derivedInterface.getOperation(pollName, null, null); + if (derivedPollOperation == null) { + derivedPollOperation = derivedInterface.createOwnedOperation(pollName, null, null); + } + + // each non-in parameter is in the poll operation. + derivedPollOperation.createOwnedParameter("timeout", null); //$NON-NLS-1$ + + for (Parameter parameter : operation.getOwnedParameters()) { + if (parameter.getDirection() != ParameterDirectionKind.IN_LITERAL) { + + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (derivedPollOperation.getOwnedParameter(paramName, paramType) == null) { + Parameter newParameter = + derivedPollOperation.createOwnedParameter(parameter.getName(), parameter.getType()); + newParameter.setDirection(parameter.getDirection()); + newParameter.setLower(parameter.getLower()); + newParameter.setUpper(parameter.getUpper()); + } + } + } + } + // each in and inout parameter is in the request operation. + for (Parameter parameter : operation.getOwnedParameters()) { + if ((parameter.getDirection() == ParameterDirectionKind.IN_LITERAL) || + (parameter.getDirection() == ParameterDirectionKind.INOUT_LITERAL)) { + + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (derivedOperation.getOwnedParameter(paramName, paramType) == null) { + Parameter newParameter = + derivedOperation.createOwnedParameter(parameter.getName(), parameter.getType()); + newParameter.setDirection(parameter.getDirection()); + newParameter.setLower(parameter.getLower()); + newParameter.setUpper(parameter.getUpper()); + } + } + } + // remove those parameters that exist in derived, but not original interface. + Iterator<Parameter> derivedParameters = derivedOperation.getOwnedParameters().iterator(); + while (derivedParameters.hasNext()) { + Parameter parameter = derivedParameters.next(); + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if ((!paramName.equals("timeout")) && //$NON-NLS-1$ + (operation.getOwnedParameter(paramName, paramType) == null)) { + // not on in original interface, remove from derived as well + derivedParameters.remove(); + } + } + } + + // check whether operations in derived interface exist in original interface + // (remove, if not) + Iterator<Operation> derivedOperations = derivedInterface.getOwnedOperations().iterator(); + while (derivedOperations.hasNext()) { + Operation derivedOperation = derivedOperations.next(); + String name = derivedOperation.getName(); + if (name == null) { + continue; + } + if (name.endsWith("Poll")) { //$NON-NLS-1$ + // remove Poll postfix + name = name.substring(0, name.length() - 4); + } + if (typingInterface.getOperation(name, null, null) == null) { + // not in typing interface, remove + derivedOperations.remove(); + } + } + + return derivedType; + } + + public static boolean hasOutParameters(Operation operation) { + for (Parameter parameter : operation.getOwnedParameters()) { + if (parameter.getDirection() != ParameterDirectionKind.IN_LITERAL) { + return true; + } + } + return false; + } + + @Override + public boolean needsUpdate(Port p) { + // TODO: insufficient condition + return (calcDerivedType(p, false) == null); + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ExtendedPort.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ExtendedPort.java new file mode 100644 index 00000000000..3d08ffae8bf --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ExtendedPort.java @@ -0,0 +1,132 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.papyrus.qompass.designer.core.OperationUtils; +import org.eclipse.papyrus.qompass.designer.core.PortUtils; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Classifier; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Port; +import org.eclipse.uml2.uml.Type; + + +/** + * An extended Port in the sense of the DDS4CCM standard: a port typed with a component + * type. Since the component typing the port can have multiple provided and required + * ports, the ports are implicitly owned by the port. Conjugation on the level of an extended + * port level will conjugate all ports of the component typing the port. + * + * The derived interface that is provided will concatenate provided port names within the + * extended port with the port name and provided a "big" interface. + * The derived interface that is required is identical, except for a conjugation on the + * extended port level. + * + * Creates a fixed template binding that binds T (of the extended Port) to the used data type + * + * Here, the idea is that the port type is a classifier, e.g. the data type that is + * transported by a DDS port. The port kind is the extended port, e.g. DDSWrite. + * + * The derived property isExtended of FCM port-kind is true, if the class representing the port-kind owns at least one + * port + * + * TODO: This class has become obsolete now, since extended DDS ports are now supported via flattening + * + */ +@Deprecated +public class ExtendedPort implements IMappingRule { + + public static final String PROV_PREFIX = "P_"; //$NON-NLS-1$ + + public static final String REQ_PREFIX = "R_"; //$NON-NLS-1$ + + @Override + public boolean needsUpdate(org.eclipse.papyrus.FCM.Port p) { + return needsUpdate(p, false) || + needsUpdate(p, true); + } + + public boolean needsUpdate(org.eclipse.papyrus.FCM.Port p, boolean isRequired) { + return true; + } + + @Override + public Type calcDerivedType(org.eclipse.papyrus.FCM.Port p, boolean update) + { + Type type = p.getType(); + if (!(type instanceof Classifier)) { + return null; + } + Class extendedPort = p.getKind().getBase_Class(); + + String prefix = extendedPort.getName() + "_" + (p.getBase_Port().isConjugated() ? REQ_PREFIX : PROV_PREFIX); //$NON-NLS-1$ + Interface derivedInterface = MapUtil.getDerivedInterface(p, prefix, true); + if (!update) { + return derivedInterface; + } + if (derivedInterface == null) { + return null; + } + /* + * TemplateSignature signature = TemplateUtils.getSignature(type.getNearestPackage()); + * if(signature != null) { + * Package model = Utils.getTop(derivedInterface); + * try { + * TemplateBinding binding = + * TemplateUtils.fixedBinding(model, extendedPort, (Classifier)type); + * Copy copy = new Copy(model, model, false); + * TemplateInstantiation ti = new TemplateInstantiation(copy, binding); + * // create a bound element of the extended port. Add bound class to derived interface class + * Class boundClass = ti.bindNamedElement(extendedPort); + * derivedInterface.getNearestPackage().getPackagedElements().add(boundClass); + * } catch (TransformationException e) { + * return null; + * } + * } + */ + // obtain first template parameter = port type + // kind.getBase_Class().getNearestPackage().getTemplateParameter(); + + for (Port port : extendedPort.getOwnedPorts()) { + Interface derivedIntf = (p.getBase_Port().isConjugated()) ? + PortUtils.getRequired(port) : + PortUtils.getProvided(port); + + if (derivedIntf != null) { + for (Operation op : derivedIntf.getAllOperations()) { + String name = port.getName() + "_" + op.getName(); //$NON-NLS-1$ + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedInterface.getOperation(name, null, null); + if (derivedOperation == null) { + derivedOperation = derivedInterface.createOwnedOperation(name, null, null); + OperationUtils.syncOperation(op, derivedOperation); + derivedOperation.setName(name); + } + else { + if (!OperationUtils.isSameOperation(derivedOperation, op, false)) { + OperationUtils.syncOperation(op, derivedOperation); + derivedOperation.setName(name); + } + } + } + } + } + return derivedInterface; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ExtendedPort2.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ExtendedPort2.java new file mode 100644 index 00000000000..f12eadcdc4c --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ExtendedPort2.java @@ -0,0 +1,138 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.papyrus.qompass.designer.core.PortUtils; +import org.eclipse.papyrus.qompass.designer.core.transformations.PrefixConstants; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Port; +import org.eclipse.uml2.uml.Type; + + +/** + * An extended Port in the sense of the DDS4CCM standard: a port typed with a component + * type (the extended port, not the data type that is transported). Since the component typing + * the port can have multiple provided and required + * ports, the ports are implicitly owned by the port. Conjugation on the level of an extended + * port level will conjugate all ports of the component typing the port. + * + * The derived interface that is provided will allow a caller to access individual ports. It + * is therefore a reference to the interfaces get_<portName> operations provided by a + * component. + * The derived interface that is required is identical, except for a conjugation on the + * extended port level. + * + * What is the difference to ExtendedPort? + * + */ +public class ExtendedPort2 implements IMappingRule { + + public static final String CONJ_PREFIX = "C2_"; //$NON-NLS-1$ + + public static final String NORM_PREFIX = "N2_"; //$NON-NLS-1$ + + @Override + public Type calcDerivedType(org.eclipse.papyrus.FCM.Port p, boolean update) { + + Type type = p.getType(); + if (!(type instanceof Class)) { + return null; + } + + Class extendedPort = (Class) type; + boolean isConjugated = p.getBase_Port().isConjugated(); + String prefix = isConjugated ? CONJ_PREFIX : NORM_PREFIX; + Class derivedClass = MapUtil.getDerivedClass(p, prefix, update); + Interface providedInterface = MapUtil.getDerivedInterface(p, prefix, update); + Interface requiredInterface = MapUtil.getDerivedInterface(p, prefix, update); + if (!update) { + return derivedClass; + } + if (derivedClass == null) { + return null; + } + for (Port port : extendedPort.getOwnedPorts()) { + // if the extended port is conjugated, each of the provided/required are (implicitly) + // conjugated [TODO: is PortUtils aware of it? - probably yes] + Interface reqIntf = PortUtils.getRequired(port); + + if (reqIntf != null) { + String name = PrefixConstants.getP_Prefix + port.getName(); + + // check whether operation already exists. Create, if not + Operation derivedOperation = requiredInterface.getOperation(name, null, null); + if (derivedOperation == null) { + derivedOperation = requiredInterface.createOwnedOperation(name, null, null); + } + } + + Interface provIntf = PortUtils.getProvided(port); + + if (provIntf != null) { + String name = PrefixConstants.getConnQ_Prefix + port.getName(); + + // check whether operation already exists. Create, if not + Operation derivedOperation = providedInterface.getOperation(name, null, null); + if (derivedOperation == null) { + derivedOperation = providedInterface.createOwnedOperation(name, null, null); + } + } + } + return derivedClass; + } + + @Override + public boolean needsUpdate(org.eclipse.papyrus.FCM.Port p) { + return needsUpdate(p, false) || + needsUpdate(p, true); + } + + public boolean needsUpdate(org.eclipse.papyrus.FCM.Port p, boolean isConjugated) { + Type type = p.getBase_Port().getType(); + if (!(type instanceof Class)) { + return false; + } + + Class extendedPort = (Class) type; + String prefix = isConjugated ? CONJ_PREFIX : NORM_PREFIX; + Interface derivedInterface = MapUtil.getDerivedInterface(p, prefix); + + if (derivedInterface == null) { + return true; + } + for (Port port : extendedPort.getOwnedPorts()) { + // if the extended port is conjugated, each of the provided/required are (implicitly) + // conjugated [TODO: is PortUtils aware of it? - probably yes] + Interface provIntf = (isConjugated) ? + PortUtils.getRequired(port) : + PortUtils.getProvided(port); + + if (provIntf != null) { + String name = PrefixConstants.getP_Prefix + port.getName(); + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedInterface.getOperation(name, null, null); + if (derivedOperation == null) { + return true; + } + } + } + return false; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ProvideInterface.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ProvideInterface.java new file mode 100644 index 00000000000..2432ddcdc42 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/ProvideInterface.java @@ -0,0 +1,37 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Type; + +public class ProvideInterface implements IMappingRule +{ + @Override + public Type calcDerivedType(Port p, boolean update) { + Type type = p.getBase_Port().getType(); + if (type instanceof Interface) { + return ((Interface) type); + } + return null; + } + + @Override + public boolean needsUpdate(Port p) { + return false; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PullConsumer.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PullConsumer.java new file mode 100644 index 00000000000..84c73d33c96 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PullConsumer.java @@ -0,0 +1,162 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.common.util.EList; +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.papyrus.qompass.designer.core.Log; +import org.eclipse.papyrus.qompass.designer.core.Utils; +import org.eclipse.papyrus.uml.tools.utils.PackageUtil; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.DataType; +import org.eclipse.uml2.uml.Element; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.NamedElement; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Package; +import org.eclipse.uml2.uml.Parameter; +import org.eclipse.uml2.uml.PrimitiveType; +import org.eclipse.uml2.uml.Signal; +import org.eclipse.uml2.uml.Type; + +/** + * Will generate a suitable callable interface pulling consumer. The port is typed with a primitive type + * or data type. The generated interface has a "<Type> pull as well as a "boolean hasData()" operation). + */ +public class PullConsumer implements IMappingRule { + + public static String PULL_I_PREFIX = "PullConsumer_"; //$NON-NLS-1$ + + public static String PULL_C_PREFIX = "CPullConsumer_"; //$NON-NLS-1$ + + public static String PULL_OP_NAME = "pull"; //$NON-NLS-1$ + + public static String HASDATA_OP_NAME = "hasData"; //$NON-NLS-1$ + + public static String RET_PAR_NAME = "ret"; //$NON-NLS-1$ + + public static String BOOL_QNAME = "corba::Boolean"; //$NON-NLS-1$ + + @Override + public boolean needsUpdate(Port p) { + Type type = p.getType(); + + if ((type instanceof PrimitiveType) || (type instanceof DataType) || (type instanceof Signal)) { + + Interface derivedInterface = MapUtil.getDerivedInterface(p, PULL_I_PREFIX); + if (derivedInterface == null) { + return true; + } + Operation derivedOperation = derivedInterface.getOperation(PULL_OP_NAME, null, null); + if (derivedOperation == null) { + return true; + } + EList<Parameter> parameters = derivedOperation.getOwnedParameters(); + if (parameters.size() != 1) { + return true; + } else { + Parameter parameter = parameters.get(0); + if (!parameter.getName().equals(RET_PAR_NAME)) { + return true; + } + if (parameter.getType() != type) { + return true; + } + } + } + return false; + } + + public static PullConsumer getInstance() { + if (instance == null) { + instance = new PullConsumer(); + } + return instance; + } + + @Override + public Type calcDerivedType(Port p, boolean update) { + org.eclipse.uml2.uml.Port umlPort = p.getBase_Port(); + Element owner = umlPort.getOwner(); + String ownerStr = ""; //$NON-NLS-1$ + if (owner instanceof NamedElement) { + ownerStr = " of class " + ((NamedElement) owner).getQualifiedName(); //$NON-NLS-1$ + } + Log.log(IStatus.INFO, Log.CALC_PORTKIND, + p.getKind().getBase_Class().getName() + " => GetRequired on " + umlPort.getName() + ownerStr); + Type type = p.getType(); + + if ((type instanceof PrimitiveType) || (type instanceof DataType) || (type instanceof Signal)) { + + Class derivedClass = MapUtil.getDerivedClass(p, PULL_C_PREFIX, update); + Interface derivedInterface = MapUtil.getDerivedInterface(p, PULL_I_PREFIX, update); + MapUtil.addUsage(derivedClass, derivedInterface); + if (!update) { + return derivedClass; + } + if (derivedInterface == null) { + return null; + } + // check whether operation already exists. Create, if not + Operation derivedOperationPull = derivedInterface.getOperation(PULL_OP_NAME, null, null); + if (derivedOperationPull == null) { + derivedOperationPull = derivedInterface.createOwnedOperation(PULL_OP_NAME, null, null, type); + } + EList<Parameter> parameters = derivedOperationPull.getOwnedParameters(); + if (parameters.size() > 0) { + Parameter parameter = parameters.get(0); + if ((parameter.getName() == null) || (!parameter.getName().equals(RET_PAR_NAME))) { + parameter.setName(RET_PAR_NAME); + } + if (parameter.getType() != type) { + parameter.setType(type); + } + } + Package model = PackageUtil.getRootPackage(umlPort); + Element element = Utils.getQualifiedElement(model, BOOL_QNAME); + Type booleanType = null; + if (element instanceof Type) { + booleanType = (Type) element; + } + + // check whether operation already exists. Create, if not + Operation derivedOperationHasData = derivedInterface.getOperation(HASDATA_OP_NAME, null, null); + if (derivedOperationHasData == null) { + derivedOperationHasData = derivedInterface.createOwnedOperation(HASDATA_OP_NAME, null, null, booleanType); + } + + parameters = derivedOperationHasData.getOwnedParameters(); + if (parameters.size() > 0) { + Parameter parameter = parameters.get(0); + if ((parameter.getName() == null) || (!parameter.getName().equals(RET_PAR_NAME))) { + parameter.setName(RET_PAR_NAME); + } + if ((booleanType != null) && (parameter.getType() != booleanType)) { + // added != null check + parameter.setType(booleanType); + } + } + + return derivedClass; + } else { + return null; + } + } + + protected static PullConsumer instance; +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushConsumer.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushConsumer.java new file mode 100644 index 00000000000..f0375f8840e --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushConsumer.java @@ -0,0 +1,110 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.common.util.EList; +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.papyrus.qompass.designer.core.Log; +import org.eclipse.uml2.uml.DataType; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Parameter; +import org.eclipse.uml2.uml.PrimitiveType; +import org.eclipse.uml2.uml.Signal; +import org.eclipse.uml2.uml.Type; + +/** + * Will generate a suitable called interface push consumer. The port is typed with a primitive type + * or data type. The generated interface has a "push (data <Type>)" operation ). + * + * The interface is identical to that of a PushProducer (and will be shared). + * + * @author ansgar + */ +public class PushConsumer implements IMappingRule { + + public static String PUSH_OP_PREFIX = "push"; //$NON-NLS-1$ + + public static String PUSH_OP_PARNAME = "data"; //$NON-NLS-1$ + + @Override + public boolean needsUpdate(Port p) { + Type type = p.getBase_Port().getType(); + + if ((type instanceof PrimitiveType) || (type instanceof DataType) || (type instanceof Signal)) { + + Interface derivedInterface = MapUtil.getDerivedInterface(p, PushProducer.PUSH_I_PREFIX); + if (derivedInterface == null) { + return true; + } + + Operation derivedOperation = derivedInterface.getOperation(PUSH_OP_PREFIX, null, null); + if (derivedOperation == null) { + return true; + } + EList<Parameter> parameters = derivedOperation.getOwnedParameters(); + if (parameters.size() != 1) { + return true; + } else { + Parameter parameter = parameters.get(0); + if (!parameter.getName().equals(PUSH_OP_PARNAME)) { + return true; + } + if (parameter.getType() != type) { + return true; + } + } + } + return false; + } + + @Override + public Type calcDerivedType(Port p, boolean update) { + Log.log(IStatus.INFO, Log.CALC_PORTKIND, + p.getKind().getBase_Class().getName() + " => GetProvided on " + p.getBase_Port().getName()); + Type type = p.getType(); + + if ((type instanceof PrimitiveType) || (type instanceof DataType) || (type instanceof Signal)) { + + Interface derivedInterface = MapUtil.getDerivedInterface(p, PushProducer.PUSH_I_PREFIX, update); + if (!update) { + return derivedInterface; + } + if (derivedInterface == null) { + // may happen, if within template (do not want creation of derived interfaces in template) + return null; + } + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedInterface.getOperation(PUSH_OP_PREFIX, null, null); + if (derivedOperation == null) { + derivedOperation = derivedInterface.createOwnedOperation(PUSH_OP_PREFIX, null, null); + } + EList<Parameter> parameters = derivedOperation.getOwnedParameters(); + if (parameters.size() == 0) { + derivedOperation.createOwnedParameter(PUSH_OP_PARNAME, type); + } else { + parameters.get(0).setName(PUSH_OP_PARNAME); + parameters.get(0).setType(type); + } + return derivedInterface; + } else { + return null; + } + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushProdPullCons.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushProdPullCons.java new file mode 100644 index 00000000000..d719977a2db --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushProdPullCons.java @@ -0,0 +1,75 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.DataType; +import org.eclipse.uml2.uml.PrimitiveType; +import org.eclipse.uml2.uml.Signal; +import org.eclipse.uml2.uml.Type; + +/** + * Create a bidirectional port with a push producer and a pull consumer + * TODO: Objective is not clear + * Will generate a suitable callable interface pulling consumer. The port is typed with a primitive type + * or data type. The generated interface has a "<Type> pull as well as a "boolean hasData()" operation). + */ +public class PushProdPullCons implements IMappingRule { + + @Override + public Type calcDerivedType(Port p, boolean update) { + Type type = p.getType(); + + if ((type instanceof PrimitiveType) || (type instanceof DataType) || (type instanceof Signal)) { + + Class derivedClass = MapUtil.getDerivedClass(p, "PushProdPullcons", update); + if (!update) { + return derivedClass; + } + + // obtain derived interface for other port kind (Caveat: some rules get the prefix from the + // name of the port kind attached to port "p" which would produce wrong results. + Type derivedInterfacePushProd = PushProducer.getInstance().calcDerivedType(p, update); + Type derivedInterfacePullCons = PullConsumer.getInstance().calcDerivedType(p, update); + + /* + if (derivedInterface == null) { + return null; + } + + if (!derivedInterface.getGenerals().contains(derivedInterfacePushProd)) { + derivedInterface.createGeneralization(derivedInterfacePushProd); + } + if (!derivedInterface.getGenerals().contains(derivedInterfacePullCons)) { + derivedInterface.createGeneralization(derivedInterfacePullCons); + } + return derivedInterface; + */ + return null; + } + else { + return null; + } + } + + @Override + public boolean needsUpdate(Port p) { + return PushProducer.getInstance().needsUpdate(p) || + PullConsumer.getInstance().needsUpdate(p); + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushProducer.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushProducer.java new file mode 100644 index 00000000000..e7933346b8f --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/PushProducer.java @@ -0,0 +1,130 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.common.util.EList; +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.papyrus.qompass.designer.core.Log; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.DataType; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Parameter; +import org.eclipse.uml2.uml.PrimitiveType; +import org.eclipse.uml2.uml.Signal; +import org.eclipse.uml2.uml.Type; + +/** + * Will generate a suitable callable interface push provider. The port is typed with a primitive type + * or data type. The generated interface has a "push (data <Type>)" operation + * + * The interface is identical to that of a PushConsumer (and will be shared). + * + * @author ansgar + */ +public class PushProducer implements IMappingRule { + + public static String PUSH_I_PREFIX = "Push_"; //$NON-NLS-1$ + + public static String PUSH_C_PREFIX = "CPush_"; //$NON-NLS-1$ + + public static String PUSH_OP_NAME = "push"; //$NON-NLS-1$ + + public static String PUSH_OP_PARNAME = "data"; //$NON-NLS-1$ + + public static PushProducer getInstance() { + if (instance == null) { + instance = new PushProducer(); + } + return instance; + } + + @Override + public boolean needsUpdate(Port p) { + Type type = p.getBase_Port().getType(); + + if ((type instanceof PrimitiveType) || (type instanceof DataType) || (type instanceof Signal)) { + + Interface derivedInterface = MapUtil.getDerivedInterface(p, PUSH_I_PREFIX); + if (derivedInterface == null) { + return true; + } + + Operation derivedOperation = derivedInterface.getOperation(PUSH_OP_NAME, null, null); + if (derivedOperation == null) { + return true; + } + EList<Parameter> parameters = derivedOperation.getOwnedParameters(); + if (parameters.size() != 1) { + return true; + } else { + Parameter parameter = parameters.get(0); + if (!parameter.getName().equals(PUSH_OP_PARNAME)) { + return true; + } + if (parameter.getType() != type) { + return true; + } + } + } + return false; + } + + @Override + public Type calcDerivedType(Port p, boolean update) { + Log.log(IStatus.INFO, Log.CALC_PORTKIND, p.getKind().getBase_Class().getName() + " => GetRequired on " + p.getBase_Port().getName()); + Type type = p.getType(); + + if ((type instanceof PrimitiveType) || (type instanceof DataType) || (type instanceof Signal)) { + + Interface derivedInterface = MapUtil.getDerivedInterface(p, PUSH_I_PREFIX, update); + Class derivedType = MapUtil.getDerivedClass(p, PUSH_C_PREFIX, update); + MapUtil.addUsage(derivedType, derivedInterface); + if (!update) { + return derivedType; + } + if (derivedInterface == null) { + // may happen, if within template (do not want creation of derived interfaces in template) + return null; + } + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedInterface.getOperation(PUSH_OP_NAME, null, null); + if (derivedOperation == null) { + derivedOperation = derivedInterface.createOwnedOperation(PUSH_OP_NAME, null, null); + } + EList<Parameter> parameters = derivedOperation.getOwnedParameters(); + if (parameters.size() == 0) { + derivedOperation.createOwnedParameter(PUSH_OP_PARNAME, type); + } else { + Parameter parameter = parameters.get(0); + if (!parameter.getName().equals(PUSH_OP_PARNAME)) { + parameter.setName(PUSH_OP_PARNAME); + } + if (parameter.getType() != type) { + parameter.setType(type); + } + } + return derivedType; + } else { + return null; + } + } + + protected static PushProducer instance; +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/TemplatePort.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/TemplatePort.java new file mode 100644 index 00000000000..62dce7e7709 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/TemplatePort.java @@ -0,0 +1,183 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.papyrus.FCM.PortKind; +import org.eclipse.papyrus.FCM.util.ITemplateMappingRule; +import org.eclipse.papyrus.qompass.designer.core.Utils; +import org.eclipse.papyrus.qompass.designer.core.templates.TemplateInstantiation; +import org.eclipse.papyrus.qompass.designer.core.templates.TemplateUtils; +import org.eclipse.papyrus.qompass.designer.core.transformations.LazyCopier; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationException; +import org.eclipse.papyrus.qompass.designer.core.transformations.filters.FixTemplateSync; +import org.eclipse.papyrus.qompass.modellibs.core.Activator; +import org.eclipse.papyrus.uml.tools.utils.PackageUtil; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Classifier; +import org.eclipse.uml2.uml.Namespace; +import org.eclipse.uml2.uml.Package; +import org.eclipse.uml2.uml.PackageableElement; +import org.eclipse.uml2.uml.Port; +import org.eclipse.uml2.uml.TemplateBinding; +import org.eclipse.uml2.uml.TemplateSignature; +import org.eclipse.uml2.uml.Type; +import org.eclipse.uml2.uml.util.UMLUtil; + + +/** + * An extended Port in the sense of the DDS4CCM standard: a port typed with a component + * type. Since the component typing the port can have multiple provided and required + * ports, the ports are implicitly owned by the port. Conjugation on the level of an extended + * port level will conjugate all ports of the component typing the port. + * + * The derived interface that is provided will concatenate provided port names within the + * extended port with the port name and provided a "big" interface. + * The derived interface that is required is identical, except for a conjugation on the + * extended port level. + * + * Creates a fixed template binding that binds T (of the extended Port) to the used data type + * + * Here, the idea is that the port type is a classifier, e.g. the data type that is + * transported by a DDS port. The port kind is the extended port, e.g. DDSWrite. + * + * The derived property isExtended of FCM port-kind is true, if the class representing the port-kind owns at least one + * port + * + * @author ansgar + * + */ +public class TemplatePort implements ITemplateMappingRule { + + @Override + public Type calcDerivedType(org.eclipse.papyrus.FCM.Port p, boolean update) { + // TODO: unify template mapping rule and normal one. + return null; + } + + @Override + public PortKind getBoundType(org.eclipse.papyrus.FCM.Port p) + { + Port port = p.getBase_Port(); + Type type = port.getType(); + if (!(type instanceof Classifier)) { + return null; + } + if (p.getKind() == null) { + return null; + } + Class extendedPort = p.getKind().getBase_Class(); + TemplateSignature signature = TemplateUtils.getSignature(extendedPort.getNearestPackage()); + Package pkgTemplate = signature.getNearestPackage(); + if (pkgTemplate != null) { + EList<Namespace> path = TemplateUtils.relativePathWithMerge(extendedPort, pkgTemplate); + + String name = pkgTemplate.getName() + "_" + type.getName(); //$NON-NLS-1$ + Package model = PackageUtil.getRootPackage(port); + Package pkg = model.getNestedPackage(name); + if (pkg == null) { + model = Utils.getFirstLevel(port); // try whether package template exists here + // required for target model with additional "root" folder + pkg = model.getNestedPackage(name); + } + if (pkg != null) { + for (Namespace pathElem : path) { + pkg = pkg.getNestedPackage(pathElem.getName()); + if (pkg == null) { + return null; + } + } + PackageableElement boundClass = pkg.getPackagedElement(extendedPort.getName()); + if (boundClass != null) { + return UMLUtil.getStereotypeApplication(boundClass, PortKind.class); + } + } + } + return null; + } + + @Override + public void updateBinding(org.eclipse.papyrus.FCM.Port p) { + Port port = p.getBase_Port(); + Type type = port.getType(); + if (!(type instanceof Classifier)) { + return; + } + Class extendedPort = p.getKind().getBase_Class(); + + TemplateSignature signature = TemplateUtils.getSignature(extendedPort.getNearestPackage()); + if (signature != null) { + Package model = PackageUtil.getRootPackage(port); + try { + TemplateBinding binding = + TemplateUtils.fixedBinding(model, extendedPort, (Classifier) type); + LazyCopier copy = new LazyCopier(model, model, false, true); + TemplateInstantiation ti = new TemplateInstantiation(copy, binding); + // remove listener synchronizing implementation, since it would add derived + // elements for the extended port itself (e.g. provided operations) + if (copy.postCopyListeners.contains(FixTemplateSync.getInstance())) { + copy.postCopyListeners.remove(FixTemplateSync.getInstance()); + } + + // create a bound element of the extended port. Add bound class to derived interface class + ti.bindElement(extendedPort); + } catch (TransformationException e) { + Activator.log.error("Could not create template binding", e); + } + } + } + + @Override + public boolean needsUpdate(org.eclipse.papyrus.FCM.Port p) { + Port port = p.getBase_Port(); + Type type = port.getType(); + if (!(type instanceof Classifier)) { + return false; + } + if (p.getKind() == null) { + return false; + } + Class extendedPort = p.getKind().getBase_Class(); + TemplateSignature signature = TemplateUtils.getSignature(extendedPort.getNearestPackage()); + Package pkgTemplate = signature.getNearestPackage(); + if (pkgTemplate != null) { + EList<Namespace> path = TemplateUtils.relativePathWithMerge(extendedPort, pkgTemplate); + + String name = pkgTemplate.getName() + "_" + type.getName(); //$NON-NLS-1$ + Package model = PackageUtil.getRootPackage(port); + Package pkg = model.getNestedPackage(name); + if (pkg == null) { + model = Utils.getFirstLevel(port); // try whether package template exists here + // required for target model with additional "root" folder + pkg = model.getNestedPackage(name); + } + if (pkg != null) { + for (Namespace pathElem : path) { + pkg = pkg.getNestedPackage(pathElem.getName()); + if (pkg == null) { + return true; + } + } + PackageableElement boundClass = pkg.getPackagedElement(extendedPort.getName()); + if (boundClass != null) { + if (UMLUtil.getStereotypeApplication(boundClass, PortKind.class) != null) { + return false; + } + } + } + } + return true; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/UseConjIntf.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/UseConjIntf.java new file mode 100644 index 00000000000..cee9f055d01 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/UseConjIntf.java @@ -0,0 +1,187 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import java.util.Iterator; + +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Parameter; +import org.eclipse.uml2.uml.ParameterDirectionKind; +import org.eclipse.uml2.uml.Type; + +/** + * Use a conjugated interface (!= conjugated port), i.e. an interface in which the roles "in" and "out" + * of each parameter of an operation are inversed. This transformation is useful in the context of transforming + * a "push" interface into a "pull" interface, i.e. instead of calling and providing values values to the + * called component via "in", the called components queries us and obtains these values as out parameters. + * TODO: This rules is currently not used, since data flow operation are currently either based on a datatype + * or a MARTE FlowPort + * + * @author ansgar + * + */ +public class UseConjIntf implements IMappingRule { + + private static final String CONJ_INTF_TYPE = "ConjIntfType_"; //$NON-NLS-1$ + private static final String CONJ_INTF = "ConjIntf_"; //$NON-NLS-1$ + + @Override + public Type calcDerivedType(Port p, boolean update) { + Type type = p.getBase_Port().getType(); + if (!(type instanceof Interface)) { + return null; + } + + Interface typingInterface = (Interface) type; + Interface derivedInterface = MapUtil.getDerivedInterface(p, CONJ_INTF, update); + Class derivedType = MapUtil.getDerivedClass(p, CONJ_INTF_TYPE, update); + if (!update) { + return derivedType; + } + if (derivedInterface == null) { + return null; + } + MapUtil.addUsage(derivedType, derivedInterface); + for (Operation operation : typingInterface.getOwnedOperations()) { + String name = operation.getName(); + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedInterface.getOperation(name, null, null); + if (derivedOperation == null) { + derivedOperation = derivedInterface.createOwnedOperation(name, null, null); + } + + // TODO: move to Copy (factor code, ensure that these values are handled in case of model copies ...) + derivedOperation.setIsAbstract(operation.isAbstract()); + derivedOperation.setIsStatic(operation.isStatic()); // (does not make sense for an interface, if true) + derivedOperation.setIsUnique(operation.isUnique()); + derivedOperation.setIsQuery(operation.isQuery()); + + for (Parameter parameter : operation.getOwnedParameters()) { + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (derivedOperation.getOwnedParameter(paramName, paramType) == null) { + Parameter newParameter = + derivedOperation.createOwnedParameter(parameter.getName(), parameter.getType()); + ParameterDirectionKind direction = parameter.getDirection(); + if (direction == ParameterDirectionKind.IN_LITERAL) { + newParameter.setDirection(ParameterDirectionKind.OUT_LITERAL); + } + else if (direction == ParameterDirectionKind.OUT_LITERAL) { + newParameter.setDirection(ParameterDirectionKind.IN_LITERAL); + } + else { + newParameter.setDirection(direction); + } + newParameter.setLower(parameter.getLower()); + newParameter.setUpper(parameter.getUpper()); + } + } + // remove those parameters that exist in derived, but not original interface. + Iterator<Parameter> derivedParameters = derivedOperation.getOwnedParameters().iterator(); + while (derivedParameters.hasNext()) { + Parameter parameter = derivedParameters.next(); + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (operation.getOwnedParameter(paramName, paramType) == null) { + // not on in original interface, remove from derived as well + derivedParameters.remove(); + } + } + } + + // check whether operations in derived interface exist in original interface + // (remove, if not) + Iterator<Operation> derivedOperations = derivedInterface.getOwnedOperations().iterator(); + while (derivedOperations.hasNext()) { + Operation derivedOperation = derivedOperations.next(); + String name = derivedOperation.getName(); + if (typingInterface.getOperation(name, null, null) == null) { + // not in typing interface, remove + if (derivedInterface.getOperations().remove(derivedOperation)) { + derivedOperations = derivedInterface.getOwnedOperations().iterator(); + } + } + } + return derivedType; + } + + @Override + public boolean needsUpdate(Port p) { + Type type = p.getType(); + if (!(type instanceof Interface)) { + return false; + } + + Interface typingInterface = (Interface) type; + Interface derivedInterface = MapUtil.getOrCreateDerivedInterface(p, CONJ_INTF); + Class derivedType = MapUtil.getOrCreateDerivedClass(p, CONJ_INTF_TYPE); + if ((derivedInterface == null) || (derivedType == null)) { + return true; + } + for (Operation operation : typingInterface.getOwnedOperations()) { + String name = operation.getName(); + + // check whether operation already exists. Create, if not + Operation derivedOperation = derivedInterface.getOperation(name, null, null); + if (derivedOperation == null) { + return true; + } + + // TODO: move to Copy (factor code, ensure that these values are handled in case of model copies ...) + derivedOperation.setIsAbstract(operation.isAbstract()); + derivedOperation.setIsStatic(operation.isStatic()); // (does not make sense for an interface, if true) + derivedOperation.setIsUnique(operation.isUnique()); + derivedOperation.setIsQuery(operation.isQuery()); + + for (Parameter parameter : operation.getOwnedParameters()) { + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (derivedOperation.getOwnedParameter(paramName, paramType) == null) { + return true; + } + } + // remove those parameters that exist in derived, but not original interface. + Iterator<Parameter> derivedParameters = derivedOperation.getOwnedParameters().iterator(); + while (derivedParameters.hasNext()) { + Parameter parameter = derivedParameters.next(); + String paramName = parameter.getName(); + Type paramType = parameter.getType(); + if (operation.getOwnedParameter(paramName, paramType) == null) { + // not on in original operation + return true; + } + } + } + + // check whether operations in derived interface exist in original interface + // (remove, if not) + Iterator<Operation> derivedOperations = derivedInterface.getOwnedOperations().iterator(); + while (derivedOperations.hasNext()) { + Operation derivedOperation = derivedOperations.next(); + String name = derivedOperation.getName(); + if (typingInterface.getOperation(name, null, null) == null) { + // not in typing interface + return true; + } + } + return false; + } +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/UseInterface.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/UseInterface.java new file mode 100644 index 00000000000..26af9111517 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/mappingrules/UseInterface.java @@ -0,0 +1,42 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ansgar Radermacher ansgar.radermacher@cea.fr + * + *****************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.mappingrules; + +import org.eclipse.papyrus.FCM.Port; +import org.eclipse.papyrus.FCM.util.IMappingRule; +import org.eclipse.papyrus.FCM.util.MapUtil; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Interface; +import org.eclipse.uml2.uml.Type; + + +public class UseInterface implements IMappingRule +{ + @Override + public Type calcDerivedType(Port p, boolean update) { + Type type = p.getType(); + if (type instanceof Interface) { + Class useType = MapUtil.getDerivedClass(p, "Use_", update); //$NON-NLS-1$ + MapUtil.addUsage(useType, (Interface) type); + return useType; + } + return null; + } + + @Override + public boolean needsUpdate(Port p) { + return false; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/AMIPull.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/AMIPull.xtend new file mode 100644 index 00000000000..d1e1fb28303 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/AMIPull.xtend @@ -0,0 +1,34 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Operation +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.Marshalling.* + +class AMIPull { + def operation_(Operation operation) ''' + «IF operation.name.endsWith("Poll")» + // comment polling function + if (!resultArrived) { + // wait for result, condition will be fired upon arrival + TimeVal timeVal = TimeVal::current () + timeout; + m_cond.waitUpto (timeout); + } + «operation.unmarshall» + } + «IF operation.type != null»return retValue;«ENDIF» + «ELSE» + // TODO: need suitable constant dimensioning + pBuffer = &buffer[500]; // grows backwards + int operationID = ID_[operation.name/]; + + // now marshall in and inout parameters via ASN.1 + «operation.marshall» + BEncAsnContent (&pBuffer, &operationID); + + pthread_t pt; + pthread_create (&pt, NULL, staticDispatch, (void *) this); + // TODO: add semaphore which assures that subsequent calls to [operation.name/] are not executed before dispatch + // has removed the parameters from the pBuffer stack (pBuffer can be corrupted). + // even worse: buffer will be deallocated even without a 2nd call! + «ENDIF» + ''' +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/AsyncCalls.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/AsyncCalls.xtend new file mode 100644 index 00000000000..fa7cb846212 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/AsyncCalls.xtend @@ -0,0 +1,57 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Operation +import org.eclipse.uml2.uml.Class +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.CppUtils.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.Marshalling.* + +class AsyncCalls { + def asyncCall(Operation operation) ''' + // TODO: need suitable constant dimensioning + pBuffer = &buffer[500]; // grows backwards + «operation.marshall» + int operationID = ID_[operation.name/]; + BEncAsnContent (&pBuffer, &operationID); + + pthread_t pt; + pthread_create (&pt, NULL, staticDispatch, (void *) this); + // TODO: add semaphore which assures that subsequent calls to «operation.name» are not executed before dispatch + // has removed the parameters from the pBuffer stack (pBuffer can be corrupted). + ''' + + def dispatch_(Class clazz) ''' + int operationID; + BDecAsnContent (&pBuffer, operationID); + switch (operationID) { + «FOR operation : clazz.ownedOperations» + case ID_«operation.name» + { + + // delegate call to executor + rconn->«operation.cppCall»; + break; + } + «ENDFOR» + } + ''' + + def dispatchWithThreadPool(Class clazz) ''' + int operationID; + BDecAsnContent (&pBuffer, operationID); + switch (operationID) { + «FOR operation : clazz.ownedOperations» + case ID_«operation.name» + { + «operation.unmarshall» + // delegate call to executor + rconn->«operation.cppCall»; + «IF operation.type != null»«operation.type.cppType» ret = «ENDIF»rconn->«operation.cppCall»; + «operation.marshallOutInout» + resultsReady = 1; + break; + } + «ENDFOR» + } + } + ''' +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/BehaviorUtil.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/BehaviorUtil.java new file mode 100644 index 00000000000..e815c2c332b --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/BehaviorUtil.java @@ -0,0 +1,111 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend; + +import org.eclipse.uml2.uml.Behavior; +import org.eclipse.uml2.uml.BehavioredClassifier; +import org.eclipse.uml2.uml.Class; +import org.eclipse.uml2.uml.Constraint; +import org.eclipse.uml2.uml.OpaqueBehavior; +import org.eclipse.uml2.uml.OpaqueExpression; +import org.eclipse.uml2.uml.Operation; +import org.eclipse.uml2.uml.Parameter; +import org.eclipse.uml2.uml.ParameterDirectionKind; +import org.eclipse.uml2.uml.Transition; +import org.eclipse.uml2.uml.Type; +import org.eclipse.uml2.uml.UMLPackage; + +public class BehaviorUtil { + private static final String DEFAULT_LANGUAGE = "C++"; //$NON-NLS-1$ + + public static void set(Behavior behavior, String selectLanguage, String textblock) { + if (behavior instanceof OpaqueBehavior) { + OpaqueBehavior ob = (OpaqueBehavior) behavior; + if (ob.getLanguages().size() == 0) { + ob.getLanguages().add(DEFAULT_LANGUAGE); + ob.getBodies().add(textblock); + } + else { + int i = 0; + for (String language : ob.getLanguages()) { + if (selectLanguage.equals(language)) { + if (i < ob.getBodies().size()) { + ob.getBodies().set(i, textblock); + } + break; + } + } + } + } + } + + public static void set(Behavior behavior, String textblock) { + set(behavior, DEFAULT_LANGUAGE, textblock); + } + + public static OpaqueBehavior createOpaqueBehavior(BehavioredClassifier clazz, Operation operation) { + OpaqueBehavior ob = (OpaqueBehavior) + clazz.createOwnedBehavior(operation.getName(), UMLPackage.eINSTANCE.getOpaqueBehavior()); + ob.setSpecification(operation); + return ob; + } + + public static OpaqueBehavior createOpaqueEffect(Transition transition) { + OpaqueBehavior ob = (OpaqueBehavior) + transition.createEffect("", UMLPackage.eINSTANCE.getOpaqueBehavior()); //$NON-NLS-1$ + return ob; + } + + public static OpaqueExpression createOpaqueExpression(Constraint constraint, String guardCode) { + OpaqueExpression oe = (OpaqueExpression) + constraint.createSpecification("", null, UMLPackage.eINSTANCE.getOpaqueExpression()); //$NON-NLS-1$ + oe.getLanguages().add(DEFAULT_LANGUAGE); + oe.getBodies().add(guardCode); + return oe; + } + + public static String body(Constraint constraint) { + if (constraint.getSpecification() instanceof OpaqueExpression) { + OpaqueExpression oe = (OpaqueExpression) constraint.getSpecification(); + if (oe.getBodies().size() > 0) { + return oe.getBodies().get(0); + } + } + return constraint.getSpecification().stringValue(); + } + + /** + * Create an operation with an operation return type + * + * @param clazz + * @param name + * @param retType + * @return + */ + public static Operation createOperation(Class clazz, String name, Type retType) { + Operation operation = clazz.createOwnedOperation(name, null, null); + if (retType != null) { + Parameter parameter = operation.createOwnedParameter("ret", retType); //$NON-NLS-1$ + parameter.setDirection(ParameterDirectionKind.RETURN_LITERAL); + } + return operation; + } + + public static String body(Behavior behavior) { + return body(behavior, DEFAULT_LANGUAGE); + } + + public static String body(Behavior behavior, String selectLanguage) { + if (behavior instanceof OpaqueBehavior) { + OpaqueBehavior ob = (OpaqueBehavior) behavior; + int i = 0; + for (String language : ob.getLanguages()) { + if (selectLanguage.equals(language)) { + if (i < ob.getBodies().size()) { + return ob.getBodies().get(i); + } + break; + } + } + } + return null; + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/CppUtils.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/CppUtils.xtend new file mode 100644 index 00000000000..fb028cc5f16 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/CppUtils.xtend @@ -0,0 +1,81 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.papyrus.qompass.designer.core.UMLTool +import org.eclipse.uml2.uml.NamedElement +import org.eclipse.uml2.uml.Operation +import org.eclipse.uml2.uml.Parameter +import org.eclipse.uml2.uml.ParameterDirectionKind +import org.eclipse.uml2.uml.Type + +class CppUtils { + + /** + * create the C++ signature for an operation (including parenthesis) + */ + public static def cppSignature(Operation operation) ''' + «operation.name»(«FOR parameter : operation.ownedParameters SEPARATOR(', ')» + «parameter.cppParameter» + «ENDFOR» + ''' + + + /** + * make a C++ call, pass all parameters except the return parameter + */ + public static def cppCall(Operation operation) ''' + «operation.name»(«FOR parameter : UMLTool.parametersNonRet(operation) SEPARATOR(', ')» + «parameter.name» + «ENDFOR») + ''' + + /** + * make a C++ call, pass all parameters except the return parameter, prefix with "return", + * if there is a return type in the operations declaration + */ + public static def returnCppCall(Operation operation) ''' + «IF (operation.type != null)»return «ENDIF»«operation.cppCall» + ''' + + public static def cppParameter(Parameter parameter) ''' + «parameter.type»«IF (parameter.direction == ParameterDirectionKind.OUT)»_out«ENDIF» «parameter.name» + ''' + + public static def cppType(Type type) ''' + «IF (type.qualifiedName == 'UMLPrimitiveTypes::Boolean')» + bool + «ELSEIF (type.qualifiedName == 'UMLPrimitiveTypes::Integer')» + int + «ELSE» + «UMLTool.dereferenceTypedef(type)» + «ENDIF» + ''' + + + public static def cppRetType(Operation operation) ''' + «IF (operation.type == null)» + void + «ELSE» + «operation.type.cppType» + «ENDIF» + ''' + + /** + * Open a set of C++ namespaces associated with the packages of of the passed named element + * TODO: use indentTab? => requires making this script recursive + * Need to include referenced types (assuming a naming convention? + */ + public static def openNamespace(NamedElement namedElement) ''' + «FOR ns : UMLTool.usedNamespaces(namedElement).reverse» + namespace «ns.name» + «ENDFOR» + ''' + + /** + * Close a set of C++ namespaces associated with the packages of of the passed named element + */ + public static def closeNamespace(NamedElement namedElement) ''' + «FOR ns : UMLTool.usedNamespaces(namedElement)» + }; // of namespace [ns.name/] + «ENDFOR» + ''' +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/FIFO.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/FIFO.xtend new file mode 100644 index 00000000000..59440deec7b --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/FIFO.xtend @@ -0,0 +1,13 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.DataType +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationContext + +class FIFO { + def activate(DataType datatype) ''' + if (m_size == 0) { + cerr << "Warning: size of FIFO is not properly configured (size = 0)" << endl; + } + m_fifo = new «TransformationContext.pkgTemplateParameter("T")»[m_size]; + ''' +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/GlobalConstants.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/GlobalConstants.xtend new file mode 100644 index 00000000000..b2a19cce408 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/GlobalConstants.xtend @@ -0,0 +1,56 @@ +/******************************************************************************* +* Copyright (c) 2014 Zeligsoft (2009) Limited and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +*******************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +class GlobalConstants { + public static val QUAL_NAME_SEP = "." + public static val FUNC_NAME_QUAL_NAME_SEP = "__" + public static val FUNC_NAME_PART_SEP = "____" + public static val INJECT_FUNC_NAME = "inject" + public static val INITIALIZE_FUNC_NAME = "initialize" + public static val STATE_FUNC_PREFIX = "state_" + public static val JUNCTION_FUNC_PREFIX = "junction_" + public static val CHOICE_FUNC_PREFIX = "choice_" + public static val ACTION_CHAIN_FUNC_PREFIX = "actionchain_" + public static val ACTION_FUNC_PREFIX = "action_" + public static val GUARD_FUNC_PREFIX = "guard_" + public static val USER_ACTION_FUNC_PREFIX = "useraction_" + public static val USER_GUARD_FUNC_PREFIX = "userguard_" + public static val EXIT_ACTION_FUNC_PREFIX = "exitaction_" + public static val ENTRY_ACTION_FUNC_PREFIX = "entryaction_" + public static val TRANS_ACTION_FUNC_PREFIX = "transitionaction_" + public static val SAVE_HISTORY_FUNC_NAME = "save_history" + public static val CHECK_HISTORY_FUNC_NAME = "check_history" + public static val CHECK_HISTORY_FUNC_PREFIX = "checkhistory_" + public static val CURRENT_STATE_FIELD_NAME = "currentState" + public static val HISTORY_TABLE_NAME = "history" + public static val UNDEFINED = "SPECIAL_INTERNAL_STATE_UNDEFINED" + public static val UNVISITED = "SPECIAL_INTERNAL_STATE_UNVISITED" + public static val STATE_TYPE_NAME = "State" + public static val PORT_TYPE_NAME = "Port" + public static val SIGNAL_TYPE_NAME = "Signal" + public static val INJECT_FUNC_PARAM = "msg" + public static val INITIALIZE_FUNC_PARAM = "msg" + public static val STATE_FUNC_PARAM = "msg" + public static val JUNC_FUNC_PARAM = "msg" + public static val CHOICE_FUNC_PARAM = "msg" + public static val CHAIN_FUNC_PARAM = "msg" + public static val ACTION_FUNC_PARAM = "msg" + public static val ACTION_DATA_VARIABLE = "rtdata" + public static val EMPTY_ACTION_COMMENT = "// (Automatically generated stub for an empty action)" + public static val MISSING_CODE_COMMENT = "// (No C++ code found for this action)" + public static val MISSING_CONSTRAINT_BODY = "false" + public static val FRESH_NAME_PREFIX = "new_" + public static val FRESH_ENTRYPOINT_NAME_PREFIX = "new_entrypoint_" + public static val FRESH_EXITPOINT_NAME_PREFIX = "new_exitpoint_" + public static val FRESH_CHOICEPOINT_NAME_PREFIX = "new_choice_" + public static val FRESH_JUNCTIONPOINT_NAME_PREFIX = "new_junction_" + public static var FRESH_TRANSITION_NAME_PREFIX = "new_transition_" + +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/Marshalling.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/Marshalling.xtend new file mode 100644 index 00000000000..02d93031b6a --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/Marshalling.xtend @@ -0,0 +1,51 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Operation +import static extension org.eclipse.papyrus.qompass.designer.core.UMLTool.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.CppUtils.cppType +import org.eclipse.uml2.uml.Parameter + +class Marshalling { + def static marshall(Operation operation) ''' + // now marshall in and inout parameters via ASN.1 + «FOR parameter : operation.parametersInInout» + «parameter.marshall» + «ENDFOR» + ''' + + def static marshallOutInout(Operation operation) ''' + // now marshall out and inout parameters via ASN.1 + «FOR parameter : operation.parametersOutInout» + «parameter.marshall» + «ENDFOR» + ''' + + def static marshall(Parameter parameter) ''' + { + «parameter.type.cppType» varName_ASN = «parameter.name»; + BEncAsnContent (&pBuffer, &varName_ASN); + } + ''' + + def static unmarshall(Operation operation) ''' + «FOR parameter : operation.parametersInInout.reverse» + «parameter.unmarshall» + «ENDFOR» + ''' + + def static unmarshallOutInout(Operation operation) ''' + «FOR parameter : operation.parametersOutInout.reverse» + «parameter.unmarshall» + «ENDFOR» + ''' + + def static unmarshall(Parameter parameter) ''' + «parameter.type.cppType» «parameter.name» + { + «parameter.type.cppType» varName_ASN; + BDecAsnContent (&pBuffer, &varName_ASN); + «parameter.name» = varName_ASN; + } + ''' + +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/MultipleReceptacle.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/MultipleReceptacle.xtend new file mode 100644 index 00000000000..0f73ff273e2 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/MultipleReceptacle.xtend @@ -0,0 +1,14 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Operation +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.CppUtils.cppCall + +class MultipleReceptacle { + def operation_(Operation operation) ''' + for (int i=0; i<9; i++) { + if (rconn[i] != 0) { + rconn[i]->«operation.cppCall»; + } + } + ''' +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StateMachineGen.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StateMachineGen.xtend new file mode 100644 index 00000000000..310526b1998 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StateMachineGen.xtend @@ -0,0 +1,412 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.emf.common.util.BasicEList +import org.eclipse.emf.common.util.EList +import org.eclipse.emf.ecore.util.EcoreUtil +import org.eclipse.papyrus.C_Cpp.Ptr +import org.eclipse.papyrus.FCM.DerivedElement +import org.eclipse.papyrus.qompass.designer.core.StUtils +import org.eclipse.papyrus.qompass.designer.core.Utils +import org.eclipse.papyrus.qompass.designer.core.UMLTool +import org.eclipse.papyrus.qompass.designer.core.extensions.IXtend +import org.eclipse.papyrus.qompass.designer.core.sync.InterfaceSync +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationContext +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationException +import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil +import org.eclipse.uml2.uml.Behavior +import org.eclipse.uml2.uml.BehavioredClassifier +import org.eclipse.uml2.uml.CallEvent +import org.eclipse.uml2.uml.Class +import org.eclipse.uml2.uml.OpaqueExpression +import org.eclipse.uml2.uml.Operation +import org.eclipse.uml2.uml.Pseudostate +import org.eclipse.uml2.uml.PseudostateKind +import org.eclipse.uml2.uml.Reception +import org.eclipse.uml2.uml.Signal +import org.eclipse.uml2.uml.SignalEvent +import org.eclipse.uml2.uml.State +import org.eclipse.uml2.uml.StateMachine +import org.eclipse.uml2.uml.TimeEvent +import org.eclipse.uml2.uml.Transition +import org.eclipse.uml2.uml.Trigger +import org.eclipse.uml2.uml.Type +import org.eclipse.uml2.uml.ValueSpecification +import org.eclipse.uml2.uml.Vertex +import org.eclipse.uml2.uml.util.UMLUtil + +import static org.eclipse.papyrus.qompass.designer.core.EnumService.* +import static org.eclipse.papyrus.qompass.designer.vsl.ParseVSL.* + +import static extension org.eclipse.papyrus.qompass.designer.core.UMLTool.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.BehaviorUtil.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.CppUtils.cppCall +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.StateMachineUtil.* +import org.eclipse.papyrus.uml.tools.utils.PackageUtil +import org.eclipse.uml2.uml.UMLPackage +import org.eclipse.uml2.uml.Enumeration + +class StateMachineGen implements IXtend { + + Class clazz + + boolean ooPattern = true; + + def getStateMachine(Class clazz) { + // organized in a loop. But effectively supports single state machine + for (smBehavior : (clazz as BehavioredClassifier).ownedBehaviors.filter[it instanceof StateMachine]) { + return smBehavior as StateMachine + } + return null + } + + def activate(Class clazz) { + val sm = clazz.stateMachine + val flattener = new UMLFlattener + flattener.transform(sm) + activate(clazz, sm) + } + + def activate(Class clazz, StateMachine sm) ''' + m_currentState = STATE_«sm.region.initialState.name»; +#ifdef SM_VERBOSE + cout << "SM «clazz.name»: in state «sm.region.initialState.name»" << endl; +#endif + for (;;) { + processEvents(); + } + ''' + + def processEvents(Class clazz) { + val sm = clazz.stateMachine + // - Option to put processElements into original class (but, would need to copy dependencies & attributes) + // create new operation in class owning the state machine. + // val operation = clazz.createOperation("processEvents", null) + // val ob = clazz.createOpaqueBehavior(operation) + // ob.set(clazz.processEventsSM(sm).toString) + // return "executor->processEvents();" + this.clazz = clazz + return clazz.processEventsSM(sm) + } + + def eventInterceptor(Operation operation) ''' + «/*TODO: need better way to detect signal*/» + «operation.eventInterceptorCommon» + event.portID = portID; + out->writeEvent(event); + + «IF (operation.type != null)»return «ENDIF»rconn->«operation.cppCall»; + ''' + + /** + * OO variant of event interceptor. Adds call event to pool and + * then calls the original method (handled by LW container) + */ + def eventInterceptorOO(Operation operation) ''' + «operation.eventInterceptorCommon» + ''' + + def eventInterceptorCommon(Operation operation) ''' + «val derivedElement = UMLUtil.getStereotypeApplication(operation, DerivedElement)» + «IF (derivedElement != null) && (derivedElement.source instanceof Reception)» + «val signal = (derivedElement.source as Reception).signal» + // create event with global signal ID + core::ContainerServices::CallEvent_ event; + event.operationID = «literal(SIGNAL_ENUM, operation.name)»; + // map signal into value-buffer and copy attributes + ::«signal.qualifiedName» * signal = (::«signal.qualifiedName» *) &event.params; + «FOR attribute : signal.ownedAttributes» + signal->«attribute.name» = «attribute.name»; + «ENDFOR» + «PackageUtil.getRootPackage(operation).declareDependencyToSignalIDs» + «UMLTool.declareDependency(TransformationContext.classifier, signal)» + «ELSE» + // create event with operationID/portID and pass call + core::ContainerServices::CallEvent_ event; + «IF ooPattern» + event.operationID = OP_ID_«operation.name»; + «ELSE» + event.operationID = ID_«operation.name»; + «ENDIF» + «ENDIF» + ''' + + def processEventsSM(BehavioredClassifier clazz, StateMachine sm) ''' + // processEvents body - generated by Qompass + // + // supports ports «FOR port : (clazz as Class).ownedPorts» «literal('PortEnum_'+clazz.name, 'port_'+clazz.name+'_'+port.name)»«ENDFOR» + + core::ContainerServices::CallEvent_ event; + int timeout; + int newState; + bool needsTrigger; + + switch(m_currentState) { + «FOR state : sm.region.subvertices» + case «literal('LStateIDs_'+clazz.name, 'STATE_'+state.name)»: + // -------- treatment of accepted events + «state.acceptableEvents» + break; + «ENDFOR» + default: + OSAL_ERROR ("Inconsistent state"); + break; + } + if (animOut != 0) { + animOut->enterState(newState, «clazz.fragment»); + } + ''' + + /* + * Pass the actual to which the port is bound. In case of a CallEvent, pass the implemented interface + * (Not the class to which the state machine is bound). This is useful in the context of components + * whose ports implement an interface + */ + def cetrigger(Operation operation) { + val intf = operation.implementsInterface + if (intf != null) { + val packageRef = intf.boundPackageRef + // declare dependency to OperationIDs enumeration + packageRef.declareDependencyToOperationIDs + '''«packageRef.qualifiedName»::ID_«operation.name»''' + } + else { + // create operationIDs literal + literal("LOperationIDs", '''OP_ID_«operation.name»''') + } + } + + // Use service for global enumerations + def setrigger(Trigger trigger) { + val se = trigger.event as SignalEvent + literal(SIGNAL_ENUM, InterfaceSync.SIG_PREFIX + se.signal.name) + } + + /** + * create code for acceptable events + * + * big restriction: will only analyse first of possibly multiple triggers + */ + def acceptableEvents(Vertex state) ''' + // loop on state + // execute action ... + timeout = -1; // no timeout by default + «FOR transition : state.outgoings» + «IF transition.triggers.size > 0» + «val trigger = transition.triggers.get(0)» + «IF (trigger.event instanceof TimeEvent)» + «val timeEvent = trigger.event as TimeEvent» + // transition «transition.name» - trigger: TimeEvent, expression «(timeEvent.when.expr as OpaqueExpression).bodies.get(0)». + timeout = «getDurationFromVSL((timeEvent.when.expr as OpaqueExpression).bodies.get(0))»/1000; + «ENDIF» + «ENDIF» + «ENDFOR» + + «IF ((state instanceof State) && (state as State).entry != null)» + // execute entry action + executor->«(state as State).entry.name»(); + «ENDIF» + + needsTrigger = true; + + «IF hasTransitionWithoutTrigger(state)» + «FOR transition : state.outgoings» + «IF transition.triggers.size == 0» + «IF transition.guard != null» + «transition.guard.specification.createGuardFct(null)» + if (executor->«transition.guard.specification.name»()) { + «ENDIF» + newState = STATE_«transition.target.name»; +#ifdef SM_VERBOSE + cout << "SM «clazz.name»: transition to state «transition.target.name»" << endl; +#endif + «IF (transition.effect != null)» + executor->«effectName(transition)»(); + «ENDIF» + needsTrigger = false; + «IF transition.guard != null» + } + «ENDIF» + «ENDIF» + «ENDFOR» + «ENDIF» + + if (needsTrigger) { + // get an event from the pool. + «IF ooPattern» + event = eventPool.readEvent(timeout); + «ELSE» + event = ep->readEvent(timeout); + «ENDIF» + } + else { + event.operationID = -1; + } + + «val allOutgoings = state.outgoings» + «FOR transition : allOutgoings» + «IF transition.triggers.size > 0» + // has «transition.triggers.size» outgoing transitions + «val trigger = transition.triggers.get(0)» + «IF (trigger.event instanceof TimeEvent)» + // transition «transition.name» - trigger: TimeEvent (there should be at most one outgoing timed transition per state). + if (event.operationID == core::ContainerServices::EventPool::ID_TIMEOUT) { + «IF transition.guard != null» + «transition.guard.specification.createGuardFct(null)» + if (executor->«transition.guard.specification.name»()) { + «ENDIF» + newState = STATE_«transition.target.name»; +#ifdef SM_VERBOSE + cout << "SM «clazz.name»: transition to state «transition.target.name»" << endl; +#endif + «IF (transition.effect != null)» + executor->«effectName(transition)»(); + «ENDIF» + «IF transition.guard != null» + } + «ENDIF» + } + «ENDIF» + «IF (trigger.event instanceof CallEvent)» + // transition «trigger.name» - trigger: CallEvent («trigger.event.name»), operation «(trigger.event as CallEvent).operation.name» + if (event.operationID == «cetrigger((trigger.event as CallEvent).operation)») { + newState = STATE_«transition.target.name»; +#ifdef SM_VERBOSE + cout << "SM «clazz.name»: transition to state «transition.target.name»" << endl; +#endif + «IF (transition.effect != null)» + executor->«effectName(transition)»(); + «ENDIF» + } + «ENDIF» + «IF (trigger.event instanceof SignalEvent)» + «val signalEvent = trigger.event as SignalEvent» + // transition «trigger.name» - trigger: SignalEvent («signalEvent.name»), signal «signalEvent.signal.name» + if (event.operationID == «setrigger(trigger)») { + «IF (transition.effect != null) || (transition.guard != null)» + // map signal to parameter section + ::«signalEvent.signal.qualifiedName» * signal = (::«signalEvent.signal.qualifiedName» *) &event.params; + «ENDIF» + «IF transition.guard != null» + «transition.guard.specification.createGuardFct(signalEvent)» + if (executor->«transition.guard.specification.name»(signal)) { + «ENDIF» + newState = STATE_«transition.target.name»; +#ifdef SM_VERBOSE + cout << "SM «clazz.name»: transition to state «transition.target.name» (due to signal «signalEvent.signal.name»)" << endl; +#endif + «IF (transition.effect != null)» + «transition.effect.addSignalParameter(signalEvent.signal)» + executor->«effectName(transition)»(«IF signalEvent.signal.attributes.size > 0»signal«ENDIF»); + «ENDIF» + // ok = EvQUEUE ; + «IF transition.guard != null» + } + «ENDIF» + } + «ENDIF» + «ENDIF» + «ENDFOR» + + if (newState != m_currentState) { + m_currentState = newState; + «IF ((state instanceof State) && (state as State).exit != null)» + // execute exit action + executor->«(state as State).exit.name»(); + «ENDIF» + + + } + ''' + + def boolean hasTransitionWithoutTrigger(Vertex state) { + for (transition : state.outgoings) { + if (transition.triggers.size == 0) { + return true; + } + } + return false; + } + + def EList<Transition> calculateTransitions(Vertex state) { + val allOutgoings = new BasicEList<Transition> + allOutgoings.addAll(state.outgoings) + for (transition : state.outgoings) { + if (transition.target instanceof Pseudostate) { + val ps = transition.target as Pseudostate + if (ps.kind == PseudostateKind.JUNCTION_LITERAL) { + allOutgoings.addAll(ps.outgoings.clone) + } + } + } + return allOutgoings + } + + /** + * Effects are moved from behaviors embedded into transitions towards behaviors of the class. Calculate the name of these + * effects. The class FilterStateMachines moves the effects (TODO: single name calculation) + */ + def effectName(Transition transition) { + // transition.containingStateMachine.name + "_" + transition.effect.name + if (transition.effect.name == null) { + throw new TransformationException( + String.format("effect of transition has no name (in SM %s)", transition.containingStateMachine.name)) + } + transition.effect.name + } + + def void addSignalParameter(Behavior behavior, Signal signal) { + if (behavior.ownedParameters.size == 0) { + val parameter = behavior.createOwnedParameter("signal", signal) + StereotypeUtil.apply(parameter, Ptr) + } + } + + def void moveBehavior(String newName, Class tmClass, Behavior effect) { + val copiedEffect = EcoreUtil.copy(effect) + if (tmClass.getOwnedOperation(newName, null, null) != null) { + // has already been added + return; + } + val operation = tmClass.createOwnedOperation(newName, null, null); + for (parameter : effect.getOwnedParameters()) { + val newParameter = EcoreUtil.copy(parameter); + operation.getOwnedParameters().add(newParameter); + StUtils.copyStereotypes(parameter, newParameter); + } + copiedEffect.setSpecification(operation); + copiedEffect.setName(newName); + tmClass.getOwnedBehaviors().add(copiedEffect); + } + + static final String CLIB_BOOL = "AnsiCLibrary::bool" + + def void createGuardFct(ValueSpecification specification, SignalEvent event) { + val name = specification.getName() + + if (clazz.getOwnedBehavior(name) != null) { + return + } + var booleanNamedElement = Utils.getQualifiedElement(TransformationContext.sourceRoot, CLIB_BOOL); + + if (booleanNamedElement instanceof Type) { + val booleanType = TransformationContext.copier.getCopy(booleanNamedElement) as Type + val operation = clazz.createOperation(name, booleanType as Type) + if (event != null) { + val sigParam = operation.createOwnedParameter("signal", event.signal) + StereotypeUtil.apply(sigParam, Ptr) + } + val ob = clazz.createOpaqueBehavior(operation) + if (specification instanceof OpaqueExpression) { + ob.getLanguages().addAll(specification.getLanguages()); + for (String body : specification.getBodies()) { + ob.getBodies().add("return " + body + ";"); + } + } + } + else { + System.out.println("was"); + } + // if (tmClass.getOwnedOperation(newName, null, null) != null) { + // copiedEffect.setSpecification(operation); + } +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StateMachineUtil.java b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StateMachineUtil.java new file mode 100644 index 00000000000..d33d2a0c9fb --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StateMachineUtil.java @@ -0,0 +1,260 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend; + +import java.util.Collection; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.papyrus.qompass.designer.core.UMLTool; +import org.eclipse.papyrus.qompass.designer.core.Utils; +import org.eclipse.papyrus.qompass.designer.core.templates.TemplateUtils; +import org.eclipse.papyrus.qompass.designer.core.transformations.TransformationContext; +import org.eclipse.papyrus.uml.tools.utils.PackageUtil; +import org.eclipse.uml2.uml.NamedElement; +import org.eclipse.uml2.uml.Package; +import org.eclipse.uml2.uml.PackageableElement; +import org.eclipse.uml2.uml.Pseudostate; +import org.eclipse.uml2.uml.PseudostateKind; +import org.eclipse.uml2.uml.Region; +import org.eclipse.uml2.uml.State; +import org.eclipse.uml2.uml.StateMachine; +import org.eclipse.uml2.uml.TemplateBinding; +import org.eclipse.uml2.uml.Transition; +import org.eclipse.uml2.uml.Type; +import org.eclipse.uml2.uml.UMLPackage; +import org.eclipse.uml2.uml.Vertex; + + +public class StateMachineUtil { + public static Region region(State state) { + EList<Region> regions = state.getRegions(); + if (regions.size() > 0) { + return regions.get(0); + } + return null; + } + + public static Region region(StateMachine stateMachine) { + EList<Region> regions = stateMachine.getRegions(); + if (regions.size() > 0) { + return regions.get(0); + } + return null; + } + + public static EList<State> subStates(State state) { + Region region = region(state); + return (region != null) ? states(region) : new BasicEList<State>(); + } + + public static EList<State> states(StateMachine stateMachine) { + Region region = region(stateMachine); + return (region != null) ? states(region) : new BasicEList<State>(); + } + + public static EList<State> states(Region region) { + EList<State> states = new BasicEList<State>(); + for (org.eclipse.uml2.uml.Vertex vertex : region.getSubvertices()) { + if (vertex instanceof State) { + states.add((State) vertex); + } + } + return states; + } + + public static EList<Pseudostate> entryPoints(State state) { + EList<Pseudostate> pseudoStates = new BasicEList<Pseudostate>(); + for (Pseudostate pseudoState : state.getConnectionPoints()) { + if (pseudoState.getKind() == PseudostateKind.ENTRY_POINT_LITERAL) { + pseudoStates.add(pseudoState); + } + } + return pseudoStates; + } + + public static EList<Pseudostate> exitPoints(State state) { + EList<Pseudostate> pseudoStates = new BasicEList<Pseudostate>(); + for (Pseudostate pseudoState : state.getConnectionPoints()) { + if (pseudoState.getKind() == PseudostateKind.EXIT_POINT_LITERAL) { + pseudoStates.add(pseudoState); + } + } + return pseudoStates; + } + + public static EList<Pseudostate> junctionPoints(Region region) { + return filteredPseudoStates(region, PseudostateKind.JUNCTION_LITERAL); + } + + public static EList<Pseudostate> choicePoints(Region region) { + return filteredPseudoStates(region, PseudostateKind.CHOICE_LITERAL); + } + + public static EList<Pseudostate> filteredPseudoStates(Region region, PseudostateKind kind) { + EList<Pseudostate> pseudoStates = new BasicEList<Pseudostate>(); + for (Vertex vertex : region.getSubvertices()) { + if (vertex instanceof Pseudostate) { + Pseudostate pseudoState = (Pseudostate) vertex; + if (pseudoState.getKind() == kind) { + pseudoStates.add(pseudoState); + } + } + } + return pseudoStates; + } + + public static Pseudostate firstPseudoState(Region region, PseudostateKind kind) { + for (Vertex vertex : region.getSubvertices()) { + if (vertex instanceof Pseudostate) { + Pseudostate pseudoState = (Pseudostate) vertex; + if (pseudoState.getKind() == kind) { + return pseudoState; + } + } + } + return null; + } + + /** + * @return The set of all transitions ending indirectly on the state. + * This is, those which end on one of the state's entry points. + */ + public static Collection<Transition> indirectIncomings(State state) { + EList<Transition> indirectIncoming = new BasicEList<Transition>(); + for (Pseudostate p : entryPoints(state)) { + for (Transition t : p.getIncomings()) { + indirectIncoming.add(t); + } + } + return indirectIncoming; + } + + /** + * @return The set of all transitions ending indirectly on the state. + * This is, those which end on one of the state's entry points. + */ + public static Collection<Transition> indirectOutgoings(State state) { + EList<Transition> indirectIncoming = new BasicEList<Transition>(); + for (Pseudostate p : entryPoints(state)) { + for (Transition t : p.getIncomings()) { + indirectIncoming.add(t); + } + } + return indirectIncoming; + } + + + /** + * @return The set of all direct and indirect incoming transitions to the state. + */ + public static Collection<Transition> allIncomings(State state) { + EList<Transition> allIncoming = new BasicEList<Transition>(); + for (Transition t : state.getIncomings()) { + allIncoming.add(t); + } + allIncoming.addAll(indirectIncomings(state)); + return allIncoming; + } + + /** + * @return The set of all direct and indirect incoming transitions to the state. + */ + public static Collection<Transition> allOutgoings(State state) { + EList<Transition> allIncoming = new BasicEList<Transition>(); + for (Transition t : state.getIncomings()) { + allIncoming.add(t); + } + allIncoming.addAll(indirectOutgoings(state)); + return allIncoming; + } + + /** + * Adds all the inner elements of a composite state to self, except for the + * initial, entry, exit and history pseudo-states. + * + * This operation moves those elements, as it removes them from their + * original container and sets their owner to the recipient. + */ + public static void moveContents(Region targetRegion, State source) { + Region sourceRegion = region(source); + EList<Vertex> vertices = new BasicEList<Vertex>(sourceRegion.getSubvertices()); + for (Vertex v : vertices) { + // move states and pseudostates + targetRegion.getSubvertices().add(v); + } + EList<Transition> transitions = new BasicEList<Transition>(sourceRegion.getTransitions()); + for (Transition t : transitions) { + targetRegion.getTransitions().add(t); + } + } + + public static Pseudostate initialState(Region region) { + return firstPseudoState(region, PseudostateKind.INITIAL_LITERAL); + } + + public static Pseudostate deepHistory(Region region) { + return firstPseudoState(region, PseudostateKind.DEEP_HISTORY_LITERAL); + } + + public static State createState(Region region, String name) { + return (State) region.createSubvertex(name, UMLPackage.eINSTANCE.getState()); + } + + public static Pseudostate createPseudostate(Region region, String name) { + return (Pseudostate) region.createSubvertex(name, UMLPackage.eINSTANCE.getPseudostate()); + } + + /** + * Return the bound package in the context of template instantiation for a given actual. + * + * This is required by the state machine template which needs to access operation IDs + * provided by the call interceptor. + * + * @param actual + * The actual for template binding + * @return + */ + public static Package boundPackageRef(Type actual) { + for (Package nestedPkg : PackageUtil.getRootPackage(actual).getNestedPackages()) { + // search for bound package templates who are bound to the same actual + // TODO: search is ambiguous, if same actual is used more than once + if (nestedPkg.getTemplateBindings().size() > 0) { + TemplateBinding binding = nestedPkg.getTemplateBindings().get(0); + if (actual == TemplateUtils.getFirstActualFromBinding(binding)) { + return nestedPkg; + } + } + } + return null; + } + + /** + * Declares a dependency from the current classifier which is produced by template instantiation + * to the element (enum) "OperationIDs", if found within the passed package. + * + * @param pkg + * @return + */ + public static void declareDependencyToOperationIDs(Package pkg) { + PackageableElement type = pkg.getPackagedElement("OperationIDs"); //$NON-NLS-1$ + if (type instanceof Type) { + UMLTool.declareDependency(TransformationContext.classifier, (Type) type); + } + } + + /** + * Declares a dependency from the current classifier which is produced by template instantiation + * to the element (enum) "SignalIDs", if found within the passed package. + * + * @param pkg + * the package in which an enumeration is looked up + * @return + */ + public static void declareDependencyToSignalIDs(Package pkg) { + NamedElement type = Utils.getQualifiedElement(pkg, "globalenums::" + SIGNAL_ENUM); //$NON-NLS-1$ + if (type instanceof Type) { + UMLTool.declareDependency(TransformationContext.classifier, (Type) type); + } + } + + static final String SIGNAL_ENUM = "SignalIDs"; +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StructBasedMarshalling.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StructBasedMarshalling.xtend new file mode 100644 index 00000000000..89d085b3937 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/StructBasedMarshalling.xtend @@ -0,0 +1,25 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Operation +import static extension org.eclipse.papyrus.qompass.designer.core.UMLTool.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.CppUtils.cppType + +// TODO: original model code does effectively do more than marshalling +class StructBasedMarshalling { + def static marshall(Operation operation) ''' + struct ParamData { + «FOR parameter : operation.parametersInInout» + «parameter.type.cppType» «parameter.name»; + «ENDFOR» + }; + Event event; + event.ID = [operation.name/]; + event.kind = CallEvent; + ParamData * data = &event.params; + «FOR parameter : operation.parametersInInout» + data->[parameter.name/] = [parameter.name/]; + «ENDFOR» + out->dispatch(event); + ''' + +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/SyncCalls.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/SyncCalls.xtend new file mode 100644 index 00000000000..96952f2f8d1 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/SyncCalls.xtend @@ -0,0 +1,13 @@ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Operation +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.CppUtils.* + +class SyncCalls { + def syncCall(Operation operation) ''' + // put pre-interceptors here + [comment type is a derived property containing the operations return type/] + «IF operation.type != null»return «ENDIF»rconn->«operation.cppCall»; + // put post-interceptors here + ''' +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLAction.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLAction.xtend new file mode 100644 index 00000000000..3cbc6f2da3a --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLAction.xtend @@ -0,0 +1,58 @@ +/******************************************************************************* +* Copyright (c) 2014 Zeligsoft, CEA and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +*******************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.uml2.uml.Behavior +import java.util.List +import java.util.ArrayList + +/** + * An action in the sense of the invocation of a behavior. + * This means that the actual behavior is not described here. The action + * is referencing the behavior and providing information how to invoke the + * behavior. + * The action has no prefix: the name of the referenced behavior needs to be prefixed, if necessary + * + * @author Ansgar (based on code from eposse) + * + */ +class UMLAction { + + /** Source UMLRT model element. */ + @Accessors Behavior origin + + // list of parameters for a call, simply a list of strings. if null => no parameters + public List<String> callParams; + + new(Behavior origin) { + this.origin = origin + } + + /** + * Add a parameter + */ + def addParam(String parameter) { + if (callParams == null) { + callParams = new ArrayList<String>() + } + callParams.add = parameter + } + + def getSpecification() { + origin.specification + } + + def getFuncName() { + origin.name + } + + override toString() '''Action(«origin.name»)''' + +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLChain.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLChain.xtend new file mode 100644 index 00000000000..05bf596b9a6 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLChain.xtend @@ -0,0 +1,91 @@ +/******************************************************************************* +* Copyright (c) 2014 Zeligsoft (2009) Limited and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +*******************************************************************************/ + +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import java.util.HashMap +import java.util.List +import java.util.Map +import org.eclipse.uml2.uml.Behavior +import org.eclipse.uml2.uml.Transition +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * A chain of state entry, exit and transition actions for a "flat" + * {@link Transition}. + * + * @author eposse + * + */ +class UMLChain { + + @Accessors List<UMLAction> actions + @Accessors Transition owner + + static Map<Transition, UMLChain> chains; + + new () { + actions = newArrayList + } + + new (List<UMLAction> actions) { + this.actions = newArrayList(actions) // We need this to be mutable. + } + + def static void initChains() { + chains = new HashMap<Transition, UMLChain>(); + } + + /** + * Return the chain of behaviors associated with a transition + * IF the chain does not exist yet, it will be created (and stored in a hash map) + * @param t + * @return + */ + def static UMLChain chain(Transition t) { + var UMLChain chain = chains.get(t) + if (chain == null) { + chain = new UMLChain() + chain.setOwner(t) + chains.put(t, chain) + } + return chain + } + + def append(UMLAction action) { + actions.add(action) + return this + } + + def append(Behavior behavior) { + if (behavior != null) { + append(new UMLAction(behavior)) + } + return this + } + + def prepend(UMLAction action) { + actions.add(0, action) + return this + } + + def prepend(Behavior behavior) { + prepend(new UMLAction(behavior)) + return this + } + + def getOwner() { + owner + } + + def setOwner(Transition t) { + owner = t + // add transition effect to action list + append(t.effect) + } +}
\ No newline at end of file diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLFlatModel2Cpp.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLFlatModel2Cpp.xtend new file mode 100644 index 00000000000..fe1b522e4bf --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLFlatModel2Cpp.xtend @@ -0,0 +1,667 @@ +/******************************************************************************* +* Copyright (c) 2014 Zeligsoft, CEA and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +*******************************************************************************/ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Port +import org.eclipse.uml2.uml.Class +import org.eclipse.uml2.uml.Property +import org.eclipse.uml2.uml.State +import org.eclipse.uml2.uml.StateMachine +import org.eclipse.uml2.uml.Transition +import java.util.Map +import org.eclipse.uml2.uml.NamedElement +import java.util.Collection +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.StateMachineUtil.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.BehaviorUtil.* +import org.eclipse.uml2.uml.UMLPackage +import org.eclipse.uml2.uml.Enumeration +import org.eclipse.uml2.uml.EnumerationLiteral +import org.eclipse.uml2.uml.Pseudostate +import org.eclipse.uml2.uml.PseudostateKind +import org.eclipse.uml2.uml.Operation +import org.eclipse.uml2.uml.OpaqueBehavior +import org.eclipse.uml2.uml.Parameter +import org.eclipse.uml2.uml.CallEvent +import org.eclipse.uml2.uml.Stereotype +import org.eclipse.uml2.uml.Type +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.UMLChain.chain +import java.util.List +import org.eclipse.papyrus.qompass.designer.core.EnumService + +/** + * This class contains the transformation from flat UML-RT state machines to the + * C/C++ language model. + * + * It implements the algorithms described in the technical report + * + * E. Posse. "Transforming flat UML-RT State Machines to a C/C++ language model". + * Technical Report ZTR-2014-EP-002, Version 2, Zeligsoft, Sep 2014. + * + * Ansgar Radermacher (CEA): translation to UML transformation + * @author eposse + */ +class UMLFlatModel2Cpp { + + /** + * The CppCodePattern that is being used for this transformation operation. + */ + + /** The source state machine to transform */ + StateMachine machine + Class clazz + + /** Elements that go into the generated model. */ + Enumeration statesDeclaration + Property currentStateField + Map<State, EnumerationLiteral> stateEnumerators + Property historyTableDeclaration + Operation saveHistoryFunction + Map<Transition, OpaqueBehavior> actionChainFunctions + Map<Pseudostate, OpaqueBehavior> junctionPointFunctions + Map<Pseudostate, OpaqueBehavior> choicePointFunctions + Map<State, OpaqueBehavior> stateFunctions + Operation injectFunc + Operation initializeFunc + Stereotype refStereo + Stereotype constStereo + + new() { + stateEnumerators = newHashMap + // userActionFunctions = newHashMap + actionChainFunctions = newHashMap + junctionPointFunctions = newHashMap + choicePointFunctions = newHashMap + stateFunctions = newHashMap + } + + /** + * This is the main method of the transformation. It performs the + * transformation by invoking methods that generate each part of the + * target language model. + */ + def transform(StateMachine m) { + machine = m + clazz = m.owner as Class + + generateStatesDeclaration + generateCurrentStateField + generateHistoryTableDeclaration + generateSaveHistoryFunction + // generateAllUserActionFunctions + generateAllActionChainFunctions + generateAllJunctionFunctions + generateAllChoicePointFunctions + generateAllStateFunctions + generateInjectFunc + generateInitializeFunc + } + + /** + * Builds an enum type for the states of the state machine. + * + * The generated code would be something like: + * + * <p><pre> + * <code>enum State { s0, s1, s1_s0, s1_s1, s2, ... };</code> + * </pre> + */ + def generateStatesDeclaration() { + + val clazz = machine.owner as Class + statesDeclaration = clazz.createNestedClassifier(GlobalConstants.STATE_TYPE_NAME, + UMLPackage.eINSTANCE.getEnumeration) as Enumeration + for (s : machine.states) { + if (s.name == null) { + s.name = "undefined" + } + val stateLiteral = statesDeclaration.createOwnedLiteral(s.name) + stateEnumerators.put(s, stateLiteral) + } + statesDeclaration.createOwnedLiteral(GlobalConstants.UNVISITED) + + // stateEnumerators.put(State.UNVISITED, unvisitedStateLiteral) + } + + /** + * Generates a field to hold the current state. + * + * The generated code would be something like: +-- * + * <p><pre> + * <code> + * State currentState; + * </code> + * </pre> + */ + def generateCurrentStateField() { + currentStateField = clazz.createOwnedAttribute(GlobalConstants.CURRENT_STATE_FIELD_NAME, statesDeclaration) + } + + /** + * Generates a declaration for the history table for the state machine. + * + * The generated code would be something like: + * + * <p> + * <code>State[] history = { UNDEFINED, ..., UNDEFINED }; + */ + def generateHistoryTableDeclaration() { + + // TODO: currentStateField already used (probably wrong!) + historyTableDeclaration = clazz.createOwnedAttribute(GlobalConstants.HISTORY_TABLE_NAME, statesDeclaration) + val numStates = machine.states.size + + // use CppArray instead? + // TODO + // currentStateField.upper = numStates + + // get constructor TODO + var ctor = clazz.getOwnedOperation(clazz.getName(), null, null) + var OpaqueBehavior ctorOB + if (ctor != null) { + ctorOB = ctor.methods.get(0) as OpaqueBehavior + } + if (ctor == null) { + ctor = clazz.createOperation(clazz.getName(), null) + ctorOB = clazz.createOpaqueBehavior(ctor) + } + + // TODO: might be more than one constructor + ctorOB.set(''' + int i = 0; + while (i<«numStates») { + «GlobalConstants.HISTORY_TABLE_NAME»[i++] = «GlobalConstants.UNVISITED»; + }''') + } + + /** + * Generates a function that saves history. + * + * The code generated is as follows: + * + * <p><pre> + * <code> + * void saveHistory(State compositeState, State subState) { + * history[compositeState] = subState; + * } + * <code> + * </pre> + * + * where <code>State</code> is the capsule's state type (an enum) and + * <code>history</code> is the capsule's history table. + * + * <p><b>Note:</b> The current implementation generates this as a normal + * function but it should be either a macro or an inline function. + * However the C/C++ language model does not currently support these. + * + * @see + * #generateStatesDeclaration + * #generateHistoryTableDeclaration + */ + def generateSaveHistoryFunction() { + + // Create the operation programmatically. Yet, it would be better + // to provide a model template for this + // [LW container-transformation + // saveHist(State compositeState, State subState) { + // historyTable[param1] = param2; + // } + saveHistoryFunction = clazz.createOwnedOperation(GlobalConstants.SAVE_HISTORY_FUNC_NAME, null, null) + saveHistoryFunction.createOwnedParameter("compositeState", stateType) + saveHistoryFunction.createOwnedParameter("subState", stateType) + // behavior might exist already (created by flattener) + var funcOB = clazz.getOwnedBehavior(GlobalConstants.SAVE_HISTORY_FUNC_NAME) + if (funcOB == null) { + funcOB = clazz.createOpaqueBehavior(saveHistoryFunction) + } + else { + funcOB.specification = saveHistoryFunction + } + funcOB.set("history[compositeState] = subState;") + } + + /** + * Generate function declarations for transition action chains. + * + * <p>Each function generated will have a sequence of calls, invoking either + * the action functions generated by {@link generateActionFunc} for + * transition, state entry and state exit actions, as well as invoking + * "save history" actions produced by the flattening transformation. + */ + def generateAllActionChainFunctions() { + for (t : machine.region.transitions) { + val f = generateActionFunc(t) + actionChainFunctions.put(t, f) + } + } + + /** + * Generates the function declaration for a single action chain. + */ + def generateActionFunc(Transition t) { + val transitionFuncName = GlobalConstants.TRANS_ACTION_FUNC_PREFIX + "__" + t.name + val transitionFunc = clazz.createOwnedOperation(transitionFuncName, null, null) + transitionFunc.createRTMessageParam(GlobalConstants.CHAIN_FUNC_PARAM) + var transitionFuncOB = t.effect + if (transitionFuncOB == null) { + transitionFuncOB = clazz.createOpaqueBehavior(transitionFunc); + } + else { + transitionFuncOB.specification = transitionFunc + clazz.ownedBehaviors.add(transitionFuncOB) + } + + val chainFuncName = GlobalConstants.ACTION_FUNC_PREFIX + "__" + t.name + val chainFunc = clazz.createOwnedOperation(chainFuncName, null, null) + chainFunc.createRTMessageParam(GlobalConstants.CHAIN_FUNC_PARAM) + val chainFuncOB = clazz.createOpaqueBehavior(chainFunc); + + // now examine chain (separate function generateActionInvocation in original code) + // Generates a call to an action, either user action or action generated by + // the transformation. + var code = "" + for (action : t.chain.actions) { + code += call(action.specification, action.callParams) + } + chainFuncOB.set(code) + return chainFuncOB as OpaqueBehavior + } + + // def Enumerator getEnum(CppEnum enum1, String string) { + // throw new UnsupportedOperationException("TODO: auto-generated method stub") + // } + /** + * Generates all functions corresponding to choice points. + */ + def generateAllChoicePointFunctions() { + for (c : machine.region.choicePoints) { + val f = generateChoiceFunc(c) + choicePointFunctions.put(c, f) + } + } + + /** + * Generates the function corresponding to a given choice point. + */ + def generateChoiceFunc(Pseudostate p) { + val funcName = GlobalConstants.CHOICE_FUNC_PREFIX + p.name + val funcDecl = clazz.createOperation(funcName, null) + val param = funcDecl.createRTMessageParam(GlobalConstants.CHOICE_FUNC_PARAM) + val funcDeclOB = clazz.createOpaqueBehavior(funcDecl) + + // if (generateGuard) { [actFunc/](param); [DestStatement] + var code = "" + for (t : p.outgoings) { + code += ''' + if («t.guard.body») { + «call(actionChainFunctions.get(t), param.name)» + // TODO: return instead + «currentStateField.name» = «getDestination(t, false)»; + } + ''' + } + funcDeclOB.set(code) + return funcDeclOB + } + + /** + * Generates all functions corresponding to junction points. + */ + def generateAllJunctionFunctions() { + for (j : machine.region.junctionPoints) { + val f = generateJunctionFunc(j) + junctionPointFunctions.put(j, f) + } + } + + /** + * Generates the function corresponding to a specific junction point. + */ + def generateJunctionFunc( + Pseudostate p + ) { + val funcName = GlobalConstants.JUNCTION_FUNC_PREFIX + p.name + val funcDecl = clazz.createOperation(funcName, null) + val funcDeclOB = clazz.createOpaqueBehavior(funcDecl) + funcDecl.createRTMessageParam(GlobalConstants.JUNC_FUNC_PARAM) + val t = p.outgoings.get(0) // There should be only one. + var code = "" + if (t != null) { + val actFunc = actionChainFunctions.get(t) + code += call(actFunc, GlobalConstants.JUNC_FUNC_PARAM) + } + code += getDestination(t, false) + funcDeclOB.set(code) + funcDeclOB + } + + /** + * Generates all functions corresponding to states. + */ + def generateAllStateFunctions() { + for (s : machine.states) { + val f = generateStateFunc(s) + stateFunctions.put(s, f) + } + } + + /** + * Generates the function corresponding to a given state. + */ + def generateStateFunc(State state) { + val name = GlobalConstants.STATE_FUNC_PREFIX + state.name + val retType = statesDeclaration + val func = clazz.createOperation(name, retType) + val param = func.createRTMessageParam(GlobalConstants.STATE_FUNC_PARAM) + val funcOB = clazz.createOpaqueBehavior(func) + val table = getPortTransitionsTable(state) + + val code = ''' + switch («getPortCond(param)») { + «FOR port : table.keySet» + case «enumeratorFor(port)»: + switch(«getSigCond(param)») { + «FOR t : table.get(port)» + «FOR trigger : t.triggers.filter[it.ports.contains(port)]» + case «enumeratorFor(trigger.event as CallEvent)»: + «IF (t.guard != null)»if (t.guard) { «ENDIF» + «actionChainFunctions.get(t).name»(«param.name»); + return «getDestination(t, false)»; + «IF (t.guard != null)»}«ENDIF» + «ENDFOR» + «ENDFOR» + } + «ENDFOR» + } + ''' + funcOB.set(code) + funcOB + } + + /** + * Builds a table that contains for each port, all the outgoing transitions + * of a given state whose trigger has that port. + * + * @param s a {@link State} + * @return a table T indexed by {@link Port}s such that for each port p, the + * entry T[p] contains all outgoing {@link Transition}s from s + * whose trigger has port p. + */ + private def getPortTransitionsTable(State s) { + val table = newHashMap + for (t : s.outgoings) { + for (trigger : t.triggers) { + for (p : trigger.ports) { + if (!table.containsKey(p)) { + table.put(p, newHashSet) + } + table.get(p).add(t) + } + } + } + return table + } + + /** + * Obtains the function call corresponding to a transition's destination. + * + * @param t - The {@link Transition} + * @param init - Whether we are looking for the destination to be obtained + * in the ized method or in the inject method. + */ + private def getDestination(Transition t, boolean init) { + var String retVal + if (t.target instanceof Pseudostate) { + val ps = t.target as Pseudostate + if (ps.kind == PseudostateKind.CHOICE_LITERAL) { + val func = choicePointFunctions.get(ps) + retVal = func.getName() + "();" + } else if (ps.kind == PseudostateKind.JUNCTION_LITERAL) { + val func = junctionPointFunctions.get(ps) + retVal = func.getName() + "();" + } + } else { + retVal = stateEnumerators.get(t.target).name + } + if (init) { + // currentStateField = << code >> + retVal = '''«currentStateField.name» = «retVal»;''' + } + retVal + } + + /** + * Generates the main 'inject' function that receives and handles events. + */ + def generateInjectFunc() { + injectFunc = clazz.createOperation(GlobalConstants.INJECT_FUNC_NAME, null) + + // injectFunc.setVirtual() + injectFunc.createRTMessageParam(GlobalConstants.INJECT_FUNC_PARAM) + val injectFuncOB = clazz.createOpaqueBehavior(injectFunc) + val code = ''' + switch(stateCond) { + «FOR s : machine.states» + case «enumFor(s)»: + «currentStateField.name» = «call(stateFunctions.get(s), GlobalConstants.INJECT_FUNC_PARAM)» + break; + «ENDFOR» + } + ''' + injectFuncOB.set(code) + injectFunc + } + + + /** + * Build the initialize function which performs the initial transition. + * + * <p>This assumes that the top level of the state machine must have an + * initial pseudo-state, and that there is exactly one outgoing transition + * from such initial point. + * + * <p> If there is no initial point, the body of the initialize method is + * empty. + */ + def generateInitializeFunc() { + initializeFunc = clazz.createOperation(GlobalConstants.INITIALIZE_FUNC_NAME, null) + + // initializeFunc.setVirtual() + initializeFunc.createRTMessageParam(GlobalConstants.INITIALIZE_FUNC_PARAM) + val initializeFuncOB = clazz.createOpaqueBehavior(initializeFunc) + + var code = "" + if (machine.region.initialState != null) { + val initialTransition = machine.region.initialState.outgoings.get(0); + if (initialTransition != null) { + val actFunc = actionChainFunctions.get(initialTransition) + if (actFunc != null) { + code += call(actFunc, GlobalConstants.INITIALIZE_FUNC_PARAM) + } + } + code += getDestination(initialTransition, true) + initializeFuncOB.set(code) + } + } + + def call(NamedElement fct, String param) ''' + «fct.name»(«param»); + ''' + + def call(NamedElement fct, List<String> params) ''' + «IF params != null» + «fct.name»(«FOR param : params SEPARATOR ', '»«param»«ENDFOR»); + «ELSE» + «fct.name»(); + «ENDIF» + ''' + + /** + * Generates the compilation unit for the state machine (*) + * + * <p><b>Notes:</b> This implementation generates only a list of elements + * to be consumed by the capsule generator which is assumed to be + * responsible for putting together the full compilation unit. + */ + /* + def generateCompilationUnit() { + // already added? + + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, statesDeclaration); + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, currentStateField) + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, historyTableDeclaration) + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, saveHistoryFunction) + for (action : userActionFunctions.keySet) { + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, userActionFunctions.get(action)) + } + for (chain : actionChainFunctions.keySet) { + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, actionChainFunctions.get(chain)) + } + for (junction : junctionPointFunctions.keySet) { + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, junctionPointFunctions.get(junction)) + } + for (choice : choicePointFunctions.keySet) { + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, choicePointFunctions.get(choice)) + } + for (state : stateFunctions.keySet) { + cppCapsuleClass.addMember(CppClass.Visibility.PRIVATE, stateFunctions.get(state)) + } + + return generatedModel + } + */ + /** + * Auxiliary methods + */ + private def getStateType() { + statesDeclaration + } + + private def getRTMessageType() { + // TODO: import umlrts instead of declaring it in model + val umlrts = clazz.getModel().getPackagedElement("umlrts") as org.eclipse.uml2.uml.Package + umlrts.getPackagedElement("RTMessage") as org.eclipse.uml2.uml.Type; + } + + private def createRTMessageParam(Operation operation, String paramName) { + val param = operation.createOwnedParameter(paramName, RTMessageType) + if (refStereo == null) { + refStereo = param.getApplicableStereotype("C_Cpp::Ref") + } + if (refStereo != null) { + param.applyStereotype(refStereo) + } + if (constStereo == null) { + constStereo = param.getApplicableStereotype("C_Cpp::Const") + } + if (constStereo != null) { + param.applyStereotype(constStereo) + } + param + } + + // TODO - why not used any more? + private def getRTDataType(Transition t) { + var Type rtdataType = null + val Collection<Type> types = newHashSet + for (trigger : t.triggers) { + val evt = trigger.event as CallEvent + val params = evt.operation.ownedParameters + if (! params.empty) { + val first = params.get(0) + types.add(first.type) + } + } + if (types.size == 1) { + val type = types.get(0) + // TODO - was - createType + rtdataType = type + } + rtdataType + } + + private def enumFor(State s) { + stateEnumerators.get(s).label + } + + private def dispatch enumeratorFor(Port port) { + val enumerator = EnumService.literal('PortID', port.name) + enumerator + } + + private def dispatch enumeratorFor(CallEvent ce) { + val operation = ce.operation + val protocolCls = EnumService.literal('ProtocolClass', (operation.getInterface().owner as NamedElement).name); + '''«protocolCls»::signal_«ce.name»''' + } + + /** + * Builds an expression to obtain the port enum id for the switch. + * + * <p>It assumes that the message parameter to the inject function is a + * pointer to the RTMessage type, as returned by {@link #getRTMessageType}, + * this is, the signature of the inject function must be: + * + * <p><pre> + * <code>void inject(UMLRTMessage * msg)</code> + * </pre> + * + * <p>It also assumes that the port id and signal id are accessible from + * this type. Basically the assumption is that the relevant definitions are + * as follows: + * + * <p> + * <pre> + * <code> + * class UMLRTMessage : ... { + * public: + * UMLRTPort * destPort; + * UMLRTSignal * signal; + * } + * + * struct UMLRTPort { + * int id; + * // ... + * } + * + * class UMLRTSignal { + * public: + * int id; + * // ... + * } + * </code> + * </pre> + * + * <p>... where the typed <code>UMLRTPortId</code> and + * <code>UMLRTSignalId</code> can be cast to the <code>Port</code> and + * <code>Signal</code> enum types generated in the state machine's class. + * + * <p>Given this assumptions, the port condition generated has the form: + * + * <p><pre><code>(Port)(msg->destPort)->id</code></pre> + * + * <p>and the signal condition is: + * + * <p><pre><code>(ProtocolX::Signal)(msg->signal)->getId()</code></pre> + * + * <p>where <code>ProtocolX</code> is the name of the port's protocol + */ + private def getPortCond(Parameter param) { + //val messagePortField = UMLRTMessage.destPort() + //val portIdField = UMLRTCommsPort.id() + //'''«param.name».«messagePortField.name»->«portIdField.name»''' + '''TODO''' + } + + private def getSigCond(Parameter param) { + //val messageSignalField = UMLRTMessage.signal() + //val signalIdAccessor = UMLRTSignal.getId() + //'''«param.name»->«messageSignalField.name»->«signalIdAccessor.name»()''' + '''TODO''' + } +} diff --git a/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLFlattener.xtend b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLFlattener.xtend new file mode 100644 index 00000000000..002469ae5b0 --- /dev/null +++ b/extraplugins/qompass/designer/org.eclipse.papyrus.qompass.modellibs.core/src/org/eclipse/papyrus/qompass/modellibs/core/xtend/UMLFlattener.xtend @@ -0,0 +1,556 @@ +/******************************************************************************* +* Copyright (c) 2014 Zeligsoft, CEA and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +*******************************************************************************/ +package org.eclipse.papyrus.qompass.modellibs.core.xtend + +import org.eclipse.uml2.uml.Behavior +import org.eclipse.uml2.uml.Class +import org.eclipse.uml2.uml.NamedElement +import org.eclipse.uml2.uml.Pseudostate +import org.eclipse.uml2.uml.PseudostateKind +import org.eclipse.uml2.uml.Region +import org.eclipse.uml2.uml.State +import org.eclipse.uml2.uml.StateMachine +import org.eclipse.uml2.uml.Transition +import org.eclipse.uml2.uml.UMLPackage +import org.eclipse.uml2.uml.Vertex + +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.BehaviorUtil.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.StateMachineUtil.* +import static extension org.eclipse.papyrus.qompass.modellibs.core.xtend.UMLChain.chain +import org.eclipse.uml2.uml.OpaqueBehavior + +/** + * This class contains the transformation for flattening UML-RT state machines + * + * It implements the algorithms described in the technical report + * + * E. Posse. "Flattening UML-RT State Machines". + * Technical Report ZTR-2014-EP-001, Version 2, Zeligsoft, Sep 2014. + * + * @author eposse + */ +class UMLFlattener { + + val FRESH_NAME_PREFIX = "p" + val FRESH_ENTRYPOINT_NAME_PREFIX = "entrypoint_" + val FRESH_EXITPOINT_NAME_PREFIX = "exitpoint_" + val FRESH_CHOICEPOINT_NAME_PREFIX = "c" + val FRESH_JUNCTIONPOINT_NAME_PREFIX = "j" + var FRESH_TRANSITION_NAME_PREFIX = "transition_" + + var freshNameCounter = 0 + var freshEntryPointNameCounter = 0 + var freshExitPointNameCounter = 0 + var freshChoicePointNameCounter = 0 + var freshJunctionPointNameCounter = 0 + var freshTransitionNameCounter = 0 + + var Behavior saveHistoryBehavior = null + var Class clazz + + /** + * Flattens a UML-RT state machine, removing composite states, and making + * explicit transitions implied by group transitions, history points and + * initial points. + * + * This is the 'main' method of the transformer. It performs the + * transformation in-place. + * + * @param m a {@link StateMachine} + * @return the flat {@link StateMachine} m with all composite states, group + * transitions, history and initial pseudo-states removed. + */ + def StateMachine transform(StateMachine m) { + var StateMachine intermediateModel + clazz = m.owner as Class + UMLChain.initChains + phase0QualifyNames(m.region, m.name) + phase1(m) + intermediateModel = phase2(m) + return intermediateModel + } + + /** + * Change the names of states and transitions: prefix them with the names + * of the stateMachine and composite states. + */ + protected def void phase0QualifyNames(Region region, String parentName) { + for (Transition transition : region.transitions) { + transition.name = parentName + '_' + transition.name + } + for (Vertex vertex : region.subvertices) { + vertex.name = parentName + '_' + vertex.name + if (vertex instanceof State) { + val State state = vertex as State + for (subRegion : state.regions) { + phase0QualifyNames(subRegion, vertex.name) + } + } + } + } + + + /** + * Makes explicit transitions implied by group transitions, history points + * and initial points. + */ + protected def StateMachine phase1(StateMachine m) { + for (State s : m.states) { + phase1ProcessState(s) + } + return m + } + + def void phase1ProcessState(State state) { + phase1ProcessSimpleState(state) + if (!state.regions.empty) { + phase1ProcessCompositeState(state) + } + } + + /** + * Moves a simple state's entry actions to its incoming transitions and + * the state's exit actions to its outgoing transitions. + * + */ + def void phase1ProcessSimpleState(State state) { + // TODO: should that be done in UML2FlatBody (need chain?) + if (state.entry != null) { + val entryOp = clazz.createOperation(entryFctName(state), null); + state.entry.name = entryFctName(state) + // add specification + state.entry.specification = entryOp; + // clazz.ownedBehaviors.add(state.entry) + for (Transition t : state.incomings) { + t.chain.append(state.entry) + } + } + if (state.exit != null) { + val exitOp = clazz.createOperation(exitFctName(state), null); + state.exit.name = exitFctName(state) + // add specification + state.exit.specification = exitOp; + // clazz.ownedBehaviors.add(state.exit) + for (Transition t : state.allOutgoings) { + t.chain.prepend(state.exit) + } + } + } + + def entryFctName(State state) { + GlobalConstants.ENTRY_ACTION_FUNC_PREFIX + GlobalConstants.FUNC_NAME_QUAL_NAME_SEP + state.name + GlobalConstants.FUNC_NAME_QUAL_NAME_SEP + state.entry.name + } + + def exitFctName(State state) { + GlobalConstants.EXIT_ACTION_FUNC_PREFIX + GlobalConstants.FUNC_NAME_QUAL_NAME_SEP + state.name + GlobalConstants.FUNC_NAME_QUAL_NAME_SEP + state.exit.name + } + + /** + * Creates entry transitions for incoming arrows ending at the composite + * state's boundary or history pseudo-state. Creates exit transitions + * implied by group transitions. + */ + def void phase1ProcessCompositeState(State state) { + + // if (state.substates.empty) { + // replaceBySimpleState(state) + // return + // } + // Deal with incoming transitions + // TODO (ar): such a state can have entry and exit as well, no? + annotateExplicitEntryTransitions(state) + createEntryPoints(state) + var historyChoicePoint = replaceHistoryPoint(state) + removeInitialTransition(state, historyChoicePoint) + + // Deal with outgoing transitions + annotateExplicitExitTransitions(state) + createExitPoints(state) + createExitTransitions(state) + + // Process sub-states + for (State subState : state.subStates) { + phase1ProcessState(subState) + } + } + + /** + * Prepends the state's entry action to each explicit entry transition + * (transition from an entry point to a sub-state). + */ + def annotateExplicitEntryTransitions(State state) { + for (Pseudostate p : state.entryPoints) { + if (!p.outgoings.empty) { + for (Transition t : p.outgoings) { + t.chain.append(state.entry) + } + } + } + } + + /** + * Creates entry points for every direct incoming transition (transition + * ending directly on the state's boundary). + */ + def createEntryPoints(State state) { + for (Transition t : state.incomings) { + val p = state.createConnectionPoint(state.newFreshEntryPointName) + p.kind = PseudostateKind.ENTRY_POINT_LITERAL + t.target = p + } //TODO: maybe remove all directIncomming? + } + + /** + * Replaces the (deep)history pseudo-state with a choice point with transitions + * to each sub-state with conditions checking the value of the history table + * entry for this composite-state. + */ + def Pseudostate replaceHistoryPoint(State state) { + + // transform history state into a choice point + val c = state.region.deepHistory; + + if (c != null) { + // No need to redirect incoming transitions, since we transform the history + // state with a choice point + c.kind = PseudostateKind.CHOICE_LITERAL; + } + else { + return null; + } + + // Create transitions from the choice point to each sub-state with the + // appropriate guard. + for (State subState : state.subStates) { + var Vertex targetVertex + if (subState.regions.size() == 0) { + targetVertex = subState + } + else { + targetVertex = subState.region.deepHistory + if (targetVertex == null) { + targetVertex = subState + } + } + + val newTransition = state.region.createTransition(state.newFreshTransitionName) + newTransition.source = c; + newTransition.target = targetVertex; + val guard = newTransition.createGuard("CheckHistory"); + guard.createOpaqueExpression('''history[«state.name»] == «subState.name»''') + if (state.entry != null) { + newTransition.chain.append(state.entry) + } + } + // If there was an initial pseudo-state, create a transition from the + // choice point to the initial state where the guard will be true if + // the composite state has not been visited before. + val initialState = state.region.initialState + if (initialState != null) { + val initialTransition = initialState.outgoings.get(0) + + val newTransition = state.region.createTransition(state.newFreshTransitionName) + newTransition.source = c; + newTransition.target = initialTransition.target; + val guard = newTransition.createGuard("CheckHistory"); + guard.createOpaqueExpression('''history[«state.name»] == «GlobalConstants.UNVISITED»''') + // TODO: not sure, if semantics is the same. + if (initialTransition.effect != null) { + // caveat: assigining the effect to the new transition will remove it from the original + // (initial will be removed later on => not a problem?) + newTransition.effect = initialTransition.effect; + newTransition.chain.append(newTransition.effect) + } + newTransition.chain.append(state.entry) + } + // If there was no initial pseudo-state, create a 'dummy' simple state + // representing "staying at the boundary", and create a transition from + // the choice point to this dummy state, where the guard is true if + // the state has not been visited before. + else { + // The name should be different name because one of the sub-states + // may have the same name as the composite. + val border = state.region.createState(state.newFreshName); + + val newTransition = state.region.createTransition(state.newFreshTransitionName); + newTransition.source = c; + newTransition.target = border; + val guard = newTransition.createGuard("Check History") + guard.createOpaqueExpression('''history[«state.name»] == «GlobalConstants.UNVISITED»''') + if (state.entry != null) { + newTransition.chain.append(state.entry) + } + } + return c + } + + /** + * Remove the initial pseudo-state and transition and create a transition + * from each entry point which does not have an explicit entry transition + * to a sub-state, to the initial state. + */ + def removeInitialTransition(State state, Pseudostate historyChoicePoint) { + val initialState = state.region.initialState + if (initialState != null) { + val initialTransition = initialState.outgoings.get(0) // There should be exactly one initial transition + for (Pseudostate p : state.entryPoints) { + if (p.outgoings.empty) { + val newTransition = + state.region.createTransition(state.newFreshTransitionName) + newTransition.source = p + newTransition.target = initialTransition.target + newTransition.chain.append(state.entry) + newTransition.chain.append(initialTransition.effect); + } + } + state.region.transitions.remove(initialTransition) + // initialTransition.targetVertex.incomingTransitions.remove(initialTransition) // TODO: this might not be enough if the initial transition ends in a pseudo-state + // state.removeInitial + } + else { + for (Pseudostate p : state.entryPoints) { + if (p.outgoings.empty) { + val newTransition = + state.region.createTransition(state.newFreshTransitionName) + newTransition.setSource(p) + newTransition.setTarget(historyChoicePoint) + newTransition.effect = state.entry; + } + } + } + } + + /** + * Annotate explicit exit transitions (transitions from a sub-state to an + * exit point) with a "history saving action", an action that sets the + * history table entry for the composite state according to the sub-state + * that is exited. + */ + def annotateExplicitExitTransitions(State state) { + for (Pseudostate p : state.exitPoints) { + if (!p.incomings.empty) { + for (Transition t : p.incomings) { + if (saveHistoryBehavior == null) { + createSaveHistoryBehavior + } + // Actions can have specific subclasses in the Zeligsoft model. Here we use a single generic class. + // Therefore we add call information explicitly. + val UMLAction saveHistoryAction = new UMLAction(saveHistoryBehavior) + saveHistoryAction.addParam = state.name + saveHistoryAction.addParam = t.source.name + t.chain.append = saveHistoryAction + if (state.exit != null) { + t.chain.append(state.exit) + } + } + } + } + } + + /** + * Create exit points for each direct outgoing transition (transition leaving + * directly from the composite state's boundary). + */ + def createExitPoints(State state) { + for (Transition t : state.outgoings) { + val p = state.createConnectionPoint(state.newFreshExitPointName); + p.kind = PseudostateKind.EXIT_POINT_LITERAL + t.source = p; + } + } + + def createSaveHistoryBehavior() { + // create an opaque behavior without body. This will be added later in the UMLFlat2Cpp operation + saveHistoryBehavior = clazz.createOwnedBehavior(GlobalConstants.SAVE_HISTORY_FUNC_NAME, + UMLPackage.eINSTANCE.getOpaqueBehavior() + ); + } + + /** + * Create explicit exit transitions implied by group transitions. For each + * group transition creates a transition from every sub-state to the + * group-transitions's source exit point. It annotates these transitions + * with a "history saving action", an action which stores the sub-state + * in the history table entry for this composite state. + */ + def createExitTransitions(State state) { + + // Iterate over exit points, looking for group transitions. + for (Pseudostate p : state.exitPoints) { + + // If the exit point doesn't have incoming transitions then there + // is at least one group transition from it. + if (p.incomings.empty) { + + // Create explicit transitions from every sub-state to this + // exit point... + for (State subState : state.subStates) { + + // ...for each group transition leaving the exit point. + for (Transition t : p.outgoings) { + + if (saveHistoryBehavior == null) { + createSaveHistoryBehavior + } + // Append the history saving action and the state's + // exit action. + val newTransition = state.region.createTransition(state.newFreshTransitionName) + newTransition.source = subState + newTransition.target = p + // make a copy (adding it to triggers list would remove it from original transition). + for (trigger : t.triggers) { + val newTrigger = newTransition.createTrigger(trigger.name) + newTrigger.event = trigger.event + } + // newTransition.triggers.addAll(t.triggers) + newTransition.guard = t.guard + val UMLAction saveHistoryAction = new UMLAction(saveHistoryBehavior) + saveHistoryAction.addParam = state.name + saveHistoryAction.addParam = subState.name + newTransition.chain.append(saveHistoryAction) + newTransition.chain.append(state.exit) + // make a copy of the effect (but: quite bad, since we might create several copies of same effect) + if (t.effect instanceof OpaqueBehavior) { + val newBehavior = newTransition.createOpaqueEffect + newBehavior.name = t.effect.name + newBehavior.set = t.effect.body + } + } + } + + // Remove the trigger and guard from the old group transition, + // but leave the transition. + // TODO: we must replace the direct group transition with an indirect group transition leaving from the exit point + for (Transition t : p.outgoings) { + t.triggers.clear + t.guard = null + t.effect = null + } + } + } + } + + /** + * Flatten composite states. + */ + protected def StateMachine phase2(StateMachine m) { + for (State s : m.states.clone) { + phase2ProcessState(s) + if (!s.subStates.empty) { + m.region.moveContents(s) + // remove regions from composite state + s.regions.clear + } + } + return m + } + + /** + * Removes entry and exit points of a simple state, making all transitions + * to and from it, direct. + */ + def dispatch void phase2ProcessState(State state) { + if (state.regions.empty) { + phase2ProcessSimpleState(state) + } + else { + phase2ProcessComplexState(state) + } + // change name afterwards (avoid that new names are taken into account recursively) + } + + /** + * Removes entry and exit points of a simple state, making all transitions + * to and from it, direct. + */ + def dispatch void phase2ProcessSimpleState(State state) { + for (Transition t : state.indirectIncomings) { + t.target = state // TODO: Potential problem: depending on the set implementation we may be modifying the iterator + } + for (Transition t : state.outgoings) { + t.source = state + } + for (Pseudostate p : state.entryPoints) { + state.connectionPoints.remove(p) + } + for (Pseudostate p : state.exitPoints) { + state.connectionPoints.remove(p) + } + } + + /** + * Replaces all entry and exit points of the composite state with junction + * points, and recursively apply this to sub-states. + */ + def dispatch void phase2ProcessComplexState(State state) { + // Replace connection points with junction points + for (Pseudostate p : state.connectionPoints.clone) { + var j = state.region.createPseudostate(GlobalConstants.JUNCTION_FUNC_PREFIX + p.name) + j.kind = PseudostateKind.JUNCTION_LITERAL + + for (Transition t : p.incomings) { + t.target = j + } + for (Transition t : p.outgoings) { + t.source = j + } + if (p.kind == PseudostateKind.ENTRY_POINT_LITERAL) { + state.connectionPoints.remove(p) + } + else if (p.kind == PseudostateKind.EXIT_POINT_LITERAL) { + state.connectionPoints.remove(p) + } + } + + // Process substates + for (State subState : state.subStates.clone) { + phase2ProcessState(subState) + if (!subState.regions.empty) { + state.region.moveContents(subState) + subState.regions.clear + } + } + } + + /** + * Auxiliary methods + */ + def String getNewFreshName(NamedElement parent) { + freshNameCounter ++ + return parent.namePrefix + FRESH_NAME_PREFIX + freshNameCounter + } + + def String getNewFreshEntryPointName(NamedElement parent) { + freshEntryPointNameCounter ++ + return parent.namePrefix + FRESH_ENTRYPOINT_NAME_PREFIX + freshEntryPointNameCounter + } + + def String getNewFreshExitPointName(NamedElement parent) { + freshExitPointNameCounter ++ + return parent.namePrefix + FRESH_EXITPOINT_NAME_PREFIX + freshExitPointNameCounter + } + + def String getNewFreshChoicePointName(NamedElement parent) { + freshChoicePointNameCounter ++ + return parent.namePrefix + FRESH_CHOICEPOINT_NAME_PREFIX + freshChoicePointNameCounter + } + + def String getNewFreshJunctionPointName(NamedElement parent) { + freshJunctionPointNameCounter ++ + return parent.namePrefix + FRESH_JUNCTIONPOINT_NAME_PREFIX + freshJunctionPointNameCounter + } + + def String getNewFreshTransitionName(NamedElement parent) { + freshTransitionNameCounter ++ + return parent.namePrefix + FRESH_TRANSITION_NAME_PREFIX + freshTransitionNameCounter + } + + def namePrefix(NamedElement ne) { + return ne.name + "_" + } +} |