Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/quickfix/RoomQuickfixProvider.java25
-rw-r--r--plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java2
-rw-r--r--plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe21
-rw-r--r--plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/converter/MultiplicityConverter.java2
-rw-r--r--plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/room/util/Multiplicities.java133
-rw-r--r--plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java144
-rw-r--r--plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/WiringValidator.java259
-rw-r--r--tests/org.eclipse.etrice.core.room.tests/models/TestBindings.room15
-rw-r--r--tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestBindings.java8
-rw-r--r--tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestDynamicActors.java15
-rw-r--r--tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestMultiplicities.java121
11 files changed, 558 insertions, 167 deletions
diff --git a/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/quickfix/RoomQuickfixProvider.java b/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/quickfix/RoomQuickfixProvider.java
index 603b84d6f..6cf9e41e5 100644
--- a/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/quickfix/RoomQuickfixProvider.java
+++ b/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/quickfix/RoomQuickfixProvider.java
@@ -38,6 +38,7 @@ import org.eclipse.etrice.core.room.MessageData;
import org.eclipse.etrice.core.room.ReferenceType;
import org.eclipse.etrice.core.room.StandardOperation;
import org.eclipse.etrice.core.validation.RoomJavaValidator;
+import org.eclipse.etrice.core.validation.WiringValidator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
@@ -146,29 +147,7 @@ public class RoomQuickfixProvider extends FSMQuickfixProvider {
});
}
- @Fix(RoomJavaValidator.ACTOR_REF_CHANGE_REF_TYPE_TO_FIXED_OR_MULT_TO_ANY)
- public void makeActorRefRefTypeFixed(final Issue issue, IssueResolutionAcceptor acceptor) {
- acceptor.accept(issue, "Make actor reference fixed", "[fixed] ActorRef...", "add.gif", new ISemanticModification() {
- @Override
- public void apply(EObject element, IModificationContext context) throws Exception {
- ActorRef ar = (ActorRef) element;
- ar.setRefType(ReferenceType.FIXED);
- }
- });
- }
-
- @Fix(RoomJavaValidator.ACTOR_REF_CHANGE_REF_TYPE_TO_FIXED_OR_MULT_TO_ANY)
- public void makeMultiplicityAny(final Issue issue, IssueResolutionAcceptor acceptor) {
- acceptor.accept(issue, "Change multiplicity to any", "ActorRef "+issue.getData()[0]+"[*]", "add.gif", new ISemanticModification() {
- @Override
- public void apply(EObject element, IModificationContext context) throws Exception {
- ActorRef ar = (ActorRef) element;
- ar.setMultiplicity(-1);
- }
- });
- }
-
- @Fix(RoomJavaValidator.ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL)
+ @Fix(WiringValidator.ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL)
public void makeActorRefRefTypeOptional(final Issue issue, IssueResolutionAcceptor acceptor) {
acceptor.accept(issue, "Make actor reference fixed", "optional ActorRef...", "add.gif", new ISemanticModification() {
@Override
diff --git a/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java b/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java
index 9e65f5529..9027e4719 100644
--- a/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java
+++ b/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/validation/AbstractRoomJavaValidator.java
@@ -9,7 +9,7 @@ import java.util.List;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.xtext.validation.ComposedChecks;
-@ComposedChecks(validators= {org.eclipse.xtext.validation.ImportUriValidator.class, org.eclipse.etrice.core.validation.ValidatorExtensionManager.class, org.eclipse.etrice.core.validation.RoomNamesAreUniqueValidator.class, org.eclipse.etrice.core.validation.InterfaceContractValidator.class})
+@ComposedChecks(validators= {org.eclipse.xtext.validation.ImportUriValidator.class, org.eclipse.etrice.core.validation.ValidatorExtensionManager.class, org.eclipse.etrice.core.validation.RoomNamesAreUniqueValidator.class, org.eclipse.etrice.core.validation.InterfaceContractValidator.class, org.eclipse.etrice.core.validation.WiringValidator.class})
public class AbstractRoomJavaValidator extends org.eclipse.etrice.core.fsm.validation.FSMJavaValidator {
@Override
diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2
index a0df7ad97..2d384d35b 100644
--- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2
+++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2
@@ -104,6 +104,7 @@ Workflow {
composedCheck = "org.eclipse.etrice.core.validation.ValidatorExtensionManager"
composedCheck = "org.eclipse.etrice.core.validation.RoomNamesAreUniqueValidator"
composedCheck = "org.eclipse.etrice.core.validation.InterfaceContractValidator"
+ composedCheck = "org.eclipse.etrice.core.validation.WiringValidator"
}
// scoping and exporting API
diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/converter/MultiplicityConverter.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/converter/MultiplicityConverter.java
index ac5d906ab..a0582556e 100644
--- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/converter/MultiplicityConverter.java
+++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/converter/MultiplicityConverter.java
@@ -43,6 +43,8 @@ public class MultiplicityConverter extends AbstractValueConverter<Integer> {
String val = string.substring(first, last);
try {
int intValue = Integer.parseInt(val);
+ if(intValue <= 0)
+ throw new ValueConverterException("multiplicity must be positive or any [*]", node, null);
return Integer.valueOf(intValue);
}
catch (NumberFormatException e) {
diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/room/util/Multiplicities.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/room/util/Multiplicities.java
new file mode 100644
index 000000000..6752c77f4
--- /dev/null
+++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/room/util/Multiplicities.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2020 protos software gmbh (http://www.protos.de).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * CONTRIBUTORS:
+ * Jan Belle (initial contribution)
+ *
+ *******************************************************************************/
+
+package org.eclipse.etrice.core.room.util;
+
+/**
+ * This class provides utility functions for calculating with multiplicities of
+ * ports and actor references. Multiplicities are represented using an
+ * {@code int} where {@code -1} denotes the special multiplicity * that
+ * symbolizes any or arbitrary multiplicity. Only non-negative multiplicities
+ * and the special * multiplicity are considered valid. Multiplicities can be
+ * thought of as the semiring of extended natural numbers.
+ */
+public class Multiplicities {
+
+ /**
+ * Compares two multiplicities. The multiplicity * is greater than every other
+ * multiplicity except itself.
+ *
+ * @param m the first multiplicity to be compared
+ * @param n the second multiplicity to be compared
+ * @return a negative integer, zero, or a positive integer as the first argument
+ * is less than, equal to, or greater than the second
+ * @throws IllegalArgumentException if one of the arguments is not a valid
+ * multiplicity
+ * @see java.util.Comparator#compare(Object, Object)
+ */
+ public static int compare(int m, int n) {
+ if (m >= 0 && n >= 0)
+ return m - n;
+ else if (m >= -1 && n >= -1)
+ return n - m;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Sums two multiplicities. The sum is * if at least one of the summands is *.
+ *
+ * @param m the first summand
+ * @param n the second summand
+ * @return the sum
+ * @throws IllegalArgumentException if one of the arguments is not a valid
+ * multiplicity
+ */
+ public static int plus(int m, int n) {
+ if (m >= 0 && n >= 0)
+ return m + n;
+ else if (m >= -1 && n >= -1)
+ return -1;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Subtracts two multiplicities if the subtrahend is less than or equal to the
+ * minuend with respect to the ordering induced by
+ * {@link Multiplicities#compare(int, int)}. The difference is always * if the
+ * minuend is *.
+ *
+ * @param m the minuend
+ * @param n the subtrahend
+ * @return the difference
+ * @throws IllegalArgumentException if one of the arguments is not a valid
+ * multiplicity or if the subtrahend is greater
+ * than the minuend
+ */
+ public static int minus(int m, int n) {
+ if (m >= 0 && n >= 0 && m >= n)
+ return m - n;
+ else if (m == -1 && n >= -1)
+ return -1;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Multiplies two multiplicities. The product is * if at least one of the
+ * factors is * and none of them is zero.
+ *
+ * @param m the first factor
+ * @param n the second factor
+ * @return the product
+ * @throws IllegalArgumentException if one the arguments is not a valid
+ * multiplicity
+ */
+ public static int times(int m, int n) {
+ if (m >= 0 && n >= 0)
+ return m * n;
+ else if (m >= -1 && n >= -1)
+ return m == 0 || n == 0 ? 0 : -1;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Computes the minimum of two multiplicities with respect to the ordering
+ * induced by {@link Multiplicities#compare(int, int)}.
+ *
+ * @param m first argument
+ * @param n second argument
+ * @return the minimum
+ * @throws IllegalArgumentException if one of the arguments is not a valid
+ * multiplicity
+ */
+ public static int minimum(int m, int n) {
+ if (m >= 0 && n >= 0)
+ return Math.min(m, n);
+ else if (m >= -1 && n >= -1)
+ return m == -1 ? n : m;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * @param m a multiplicity
+ * @return the string representation of the multiplicity
+ * @throws IllegalArgumentException if the argument is not a valid multiplicity
+ */
+ public static String toString(int m) {
+ if (m >= 0)
+ return Integer.toString(m);
+ else if (m == -1)
+ return "*";
+ throw new IllegalArgumentException();
+ }
+}
diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java
index 9334c4863..4d3cfbed1 100644
--- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java
+++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java
@@ -50,7 +50,6 @@ import org.eclipse.etrice.core.room.ActorInstanceMapping;
import org.eclipse.etrice.core.room.ActorRef;
import org.eclipse.etrice.core.room.Attribute;
import org.eclipse.etrice.core.room.Binding;
-import org.eclipse.etrice.core.room.BindingEndPoint;
import org.eclipse.etrice.core.room.CommunicationType;
import org.eclipse.etrice.core.room.CompoundProtocolClass;
import org.eclipse.etrice.core.room.DataClass;
@@ -76,7 +75,6 @@ import org.eclipse.etrice.core.room.RoomModel;
import org.eclipse.etrice.core.room.RoomPackage;
import org.eclipse.etrice.core.room.ServiceImplementation;
import org.eclipse.etrice.core.room.StandardOperation;
-import org.eclipse.etrice.core.room.StructureClass;
import org.eclipse.etrice.core.room.SubSystemClass;
import org.eclipse.etrice.core.room.util.RoomHelpers;
import org.eclipse.etrice.generator.base.io.IModelPath;
@@ -111,19 +109,12 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator {
@Inject ImportHelpers importHelpers;
- /* message strings */
- public static final String OPTIONAL_REFS_HAVE_TO_HAVE_MULTIPLICITY_ANY = "optional refs have to have multiplicity any [*]";
- public static final String MULTIPLICITY_ANY_REQUIRES_OPTIONAL = "multiplicity any [*] requires optional";
- public static final String A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER = "a replicated port must have at most one replicated peer (with arbitrary multiplicity each)";
-
/* tags for quick fixes */
public static final String WRONG_MODEL_NAME = "RoomJavaValidator.WrongModelName";
public static final String THREAD_MISSING = "RoomJavaValidator.ThreadMissing";
public static final String DUPLICATE_ACTOR_INSTANCE_MAPPING = "RoomJavaValidator.DuplicateActorInstanceMapping";
public static final String WRONG_NAMESPACE = "RoomJavaValidator.WrongNamespace";
public static final String CIRCULAR_CONTAINMENT = "RoomJavaValidator.CircularContainment";
- public static final String ACTOR_REF_CHANGE_REF_TYPE_TO_FIXED_OR_MULT_TO_ANY = "RoomJavaValidator.ActorRefChangeRefTypeToFixed";
- public static final String ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL = "RoomJavaValidator.ActorRefChangeRefTypeToOptional";
public static final String CHANGE_DESTRUCTOR_NAME = "RoomJavaValidator.ChangeDestructorName";
public static final String CHANGE_CONSTRUCTOR_NAME = "RoomJavaValidator.ChangeConstructorName";
public static final String INVALID_ANNOTATION_TARGET = "RoomJavaValidator.InvalidAnnotationTarget";
@@ -221,40 +212,6 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator {
if (ar.getRefType()==ReferenceType.FIXED && ar.getType().isAbstract()) {
error("A (fixed) actor reference must not be of an abstract type", null);
}
-
- // check actor ref array upper bound
- if (ar.getMultiplicity()<0) {
- // multiplicity * requires optional
- if (ar.getRefType()!=ReferenceType.OPTIONAL)
- error(MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
- RoomPackage.eINSTANCE.getActorRef_RefType(), ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL);
- }
- else if (ar.getMultiplicity()>1) {
- // fixed multiplicity requires fixed type
- if (ar.getRefType()==ReferenceType.OPTIONAL)
- error(OPTIONAL_REFS_HAVE_TO_HAVE_MULTIPLICITY_ANY,
- RoomPackage.eINSTANCE.getActorRef_RefType(), ACTOR_REF_CHANGE_REF_TYPE_TO_FIXED_OR_MULT_TO_ANY, ar.getName());
- }
-
- // check actor ref array has ports with fixed multiplicity
- if (ar!=null) {
- ActorClass ac = ar.getType();
- if (ar.getMultiplicity()>1) {
- for (Port p : ac.getInterfacePorts()) {
- if (p.getMultiplicity()<0) {
- //int idx = ((ActorContainerClass)ar.eContainer()).getActorRefs().indexOf(ar);
- error("replicated actor must not have replicated port with arbitrary multiplicity", null);
- }
- }
- }
- }
- }
-
- @Check
- public void checkLayerConnectiontarget(LayerConnection lc) {
- if (lc.getTo().getRef() instanceof ActorRef)
- if (((ActorRef)lc.getTo().getRef()).getMultiplicity()>1)
- error("layer connection must not connect to replicated actor", null);
}
@Check
@@ -431,38 +388,6 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator {
}
@Check
- public void checkPortCompatibility(Binding bind) {
- // don't validate if unresolved, this is already be an error
- if(EcoreUtil.ExternalCrossReferencer.find(bind).keySet().stream().anyMatch(eObj -> eObj.eIsProxy()))
- return;
- // don't validate if protocols are missing or null (NPE), this is an error at the interface item
- if(bind.getEndpoint1().getPort().getProtocol().eIsProxy() || bind.getEndpoint2().getPort().getProtocol().eIsProxy())
- return;
-
- Result result = validationUtil.isValid(bind);
- if (!result.isOk()) {
- error(result.getMsg(), bind, null);
- }
- }
-
- @Check
- public void checkServiceCompatibility(LayerConnection conn) {
- // don't validate if unresolved, this is already an error
- if(EcoreUtil.ExternalCrossReferencer.find(conn).keySet().stream().anyMatch(eObj -> eObj.eIsProxy()))
- return;
- // don't validate if protocols are missing or null (NPE), this is an error at the interface item
- if(conn.getFrom() instanceof RelaySAPoint && ((RelaySAPoint) conn.getFrom()).getRelay().getProtocol().eIsProxy())
- return;
- if(conn.getTo().getService().getProtocol().eIsProxy())
- return;
-
- Result result = validationUtil.isValid(conn);
- if (!result.isOk()) {
- error(result.getMsg(), RoomPackage.eINSTANCE.getLayerConnection_From());
- }
- }
-
- @Check
public void checkPortCommunicationCompatibility(ActorClass ac){
if(ac.getCommType() == ComponentCommunicationType.SYNCHRONOUS){
// not supported yet
@@ -508,16 +433,41 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator {
}
}
}
+
+ @Check
+ public void checkPortCompatibility(Binding bind) {
+ // don't validate if unresolved, this is already an error
+ if (EcoreUtil.ExternalCrossReferencer.find(bind).keySet().stream().anyMatch(eObj -> eObj.eIsProxy()))
+ return;
+ // don't validate if protocols are missing or null (NPE), this is an error at
+ // the interface item
+ if (bind.getEndpoint1().getPort().getProtocol().eIsProxy()
+ || bind.getEndpoint2().getPort().getProtocol().eIsProxy())
+ return;
+
+ Result result = validationUtil.isValid(bind);
+ if (!result.isOk()) {
+ error(result.getMsg(), bind, null);
+ }
+ }
@Check
- public void checkPort(Port port) {
- if (port.getMultiplicity()==0)
- error("multiplicity must not be 0", RoomPackage.eINSTANCE.getPort_Multiplicity());
- if (port.getMultiplicity()<-1)
- error("multiplicity must be -1 or positive", RoomPackage.eINSTANCE.getPort_Multiplicity());
- if (port.getProtocol() instanceof ProtocolClass)
- if (((ProtocolClass)port.getProtocol()).getCommType()==CommunicationType.DATA_DRIVEN && port.getMultiplicity()!=1)
- error("multiplicity must be 1 for data driven ports", RoomPackage.eINSTANCE.getPort_Multiplicity());
+ public void checkServiceCompatibility(LayerConnection conn) {
+ // don't validate if unresolved, this is already an error
+ if (EcoreUtil.ExternalCrossReferencer.find(conn).keySet().stream().anyMatch(eObj -> eObj.eIsProxy()))
+ return;
+ // don't validate if protocols are missing or null (NPE), this is an error at
+ // the interface item
+ if (conn.getFrom() instanceof RelaySAPoint
+ && ((RelaySAPoint) conn.getFrom()).getRelay().getProtocol().eIsProxy())
+ return;
+ if (conn.getTo().getService().getProtocol().eIsProxy())
+ return;
+
+ Result result = validationUtil.isValid(conn);
+ if (!result.isOk()) {
+ error(result.getMsg(), RoomPackage.eINSTANCE.getLayerConnection_From());
+ }
}
@Check
@@ -698,34 +648,6 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator {
}
@Check
- public void checkReplicatedPortBindingPatterns(StructureClass sc) {
- HashSet<String> haveReplPeer = new HashSet<String>();
- for (Binding bind : sc.getBindings()) {
- // don't validate if unresolved, this is already be an error
- if(EcoreUtil.ExternalCrossReferencer.find(bind).keySet().stream().anyMatch(eObj -> eObj.eIsProxy()))
- continue;
-
- Port p1 = bind.getEndpoint1().getPort();
- Port p2 = bind.getEndpoint2().getPort();
- if (p1.getMultiplicity()<0 && p2.getMultiplicity()<0) {
- if (!haveReplPeer.add(getEndpointKey(bind.getEndpoint1())))
- error(A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER,
- bind,
- RoomPackage.Literals.BINDING__ENDPOINT1);
- if (!haveReplPeer.add(getEndpointKey(bind.getEndpoint2())))
- error(A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER,
- bind,
- RoomPackage.Literals.BINDING__ENDPOINT2);
- }
- }
- }
-
- private String getEndpointKey(BindingEndPoint ep) {
- String ref = ep.getActorRef()==null? "" : ep.getActorRef().getName();
- return ref + "#" + ep.getPort().getName();
- }
-
- @Check
public void checkPortClassContents(PortClass pc) {
if (pc.getAttributes().isEmpty() && pc.getMsgHandlers().isEmpty() && pc.getOperations().isEmpty())
error("port class must not be empty", pc, RoomPackage.Literals.PORT_CLASS__ATTRIBUTES);
diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/WiringValidator.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/WiringValidator.java
new file mode 100644
index 000000000..629b0b2dd
--- /dev/null
+++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/WiringValidator.java
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * Copyright (c) 2020 protos software gmbh (http://www.protos.de).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * CONTRIBUTORS:
+ * Jan Belle (initial contribution)
+ *
+ *******************************************************************************/
+
+package org.eclipse.etrice.core.validation;
+
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import org.eclipse.etrice.core.room.ActorClass;
+import org.eclipse.etrice.core.room.ActorContainerRef;
+import org.eclipse.etrice.core.room.ActorRef;
+import org.eclipse.etrice.core.room.Binding;
+import org.eclipse.etrice.core.room.BindingEndPoint;
+import org.eclipse.etrice.core.room.CommunicationType;
+import org.eclipse.etrice.core.room.LayerConnection;
+import org.eclipse.etrice.core.room.LogicalSystem;
+import org.eclipse.etrice.core.room.Port;
+import org.eclipse.etrice.core.room.ProtocolClass;
+import org.eclipse.etrice.core.room.ReferenceType;
+import org.eclipse.etrice.core.room.RoomPackage;
+import org.eclipse.etrice.core.room.SubSystemClass;
+import org.eclipse.etrice.core.room.util.Multiplicities;
+import org.eclipse.etrice.core.room.util.RoomHelpers;
+import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
+import org.eclipse.xtext.validation.Check;
+import org.eclipse.xtext.validation.EValidatorRegistrar;
+
+/**
+ * This validator enforces the constraints that are required by the wiring
+ * algorithm. All relay ports must be implemented in the sense that they are
+ * connected to as many end ports as their multiplicity implies. Further the
+ * multiplicity of every end point must not be exceeded. The latter restriction
+ * ensures that the wiring algorithm can act greedily while the former enables
+ * the validation algorithm to consider every actor class individually.
+ */
+public class WiringValidator extends AbstractDeclarativeValidator {
+
+ public static final String MULTIPLICITY_ANY_REQUIRES_OPTIONAL = "multiplicity any [*] requires optional";
+
+ public static final String ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL = "MultiplicityValidator.ActorRefChangeRefTypeToOptional";
+
+ @Check
+ public void checkDataDrivenPortMultiplicity(Port port) {
+ if (port.getProtocol() instanceof ProtocolClass
+ && ((ProtocolClass) port.getProtocol()).getCommType() == CommunicationType.DATA_DRIVEN
+ && port.getMultiplicity() != 1)
+ error("multiplicity must be 1 for data driven ports", RoomPackage.eINSTANCE.getPort_Multiplicity());
+ }
+
+ @Check
+ public void checkActorRefMultiplicity(ActorRef ref) {
+ if (ref.getMultiplicity() == -1 && ref.getRefType() != ReferenceType.OPTIONAL)
+ error(MULTIPLICITY_ANY_REQUIRES_OPTIONAL, RoomPackage.eINSTANCE.getActorRef_RefType(),
+ ACTOR_REF_CHANGE_REF_TYPE_TO_OPTIONAL);
+ if (ref.getMultiplicity() != 1) {
+ ActorClass ac = ref.getType();
+ if (getAll(ac, ActorClass::getInterfacePorts).anyMatch(port -> port.getMultiplicity() == -1))
+ error("replicated actors must not have replicated ports with multiplicity any", null);
+ if (getAll(ac, ActorClass::getServiceProvisionPoints).anyMatch(spp -> true))
+ error("replicated actors must not have service provision points", null);
+ }
+ }
+
+ @Check
+ public void checkLayerConnectionTarget(LayerConnection lc) {
+ if (lc.getTo().getRef() instanceof ActorRef)
+ if (((ActorRef) lc.getTo().getRef()).getMultiplicity() > 1)
+ error("layer connection must not connect to replicated actor", null);
+ }
+
+ @Check
+ public void checkPortMultiplicities(LogicalSystem ls) {
+ checkPortMultiplicities(ls.getBindings().stream(), Stream.empty());
+ }
+
+ @Check
+ public void checkPortMultiplicities(SubSystemClass ssc) {
+ checkPortMultiplicities(ssc.getBindings().stream(), ssc.getRelayPorts().stream());
+ }
+
+ @Check
+ public void checkPortMultiplicities(ActorClass ac) {
+ checkPortMultiplicities(getAll(ac, ActorClass::getBindings),
+ ac.isAbstract() ? Stream.empty() : getAll(ac, ActorClass::getRelayPorts));
+ }
+
+ /**
+ * Computes the required multiplicity for each end point using the given
+ * bindings and then compares them to the actual multiplicity of the end points.
+ *
+ * @param bindings
+ * all bindings to consider when computing the required
+ * multiplicities
+ * @param relays
+ * all relay ports to check for implementation
+ */
+ private void checkPortMultiplicities(Stream<Binding> bindings, Stream<Port> relays) {
+ // Compute the required multiplicity for each end point.
+ Map<EndPoint, Integer> multiplicities = new HashMap<>();
+ bindings.forEach(binding -> {
+ EndPoint ep1 = new EndPoint(binding.getEndpoint1());
+ EndPoint ep2 = new EndPoint(binding.getEndpoint2());
+ int multiplicity = Multiplicities.minimum(getMultiplicity(ep1), getMultiplicity(ep2));
+ multiplicities.merge(ep1, multiplicity, Multiplicities::plus);
+ multiplicities.merge(ep2, multiplicity, Multiplicities::plus);
+ });
+
+ // Check whether the multiplicity of any end point is exceeded.
+ multiplicities.entrySet().forEach(entry -> {
+ EndPoint ep = entry.getKey();
+ int multiplicity = getMultiplicity(ep);
+ int calculated = entry.getValue();
+ if (Multiplicities.compare(calculated, multiplicity) > 0) {
+ if (multiplicity == 1)
+ warning("port \"" + ep.toString() + "\" is connected to more than one peer",
+ RoomPackage.eINSTANCE.getRoomClass_Name());
+ else
+ warning("port \"" + ep.toString() + "\" is connected to " + Multiplicities.toString(calculated)
+ + " peers but its multiplicity is only " + Multiplicities.toString(multiplicity),
+ RoomPackage.eINSTANCE.getRoomClass_Name());
+ }
+ });
+
+ // Check whether every relay port is implemented.
+ relays.map(EndPoint::new).forEach(ep -> {
+ int multiplicity = getMultiplicity(ep);
+ int calculated = multiplicities.getOrDefault(ep, 0);
+ if (Multiplicities.compare(calculated, multiplicity) < 0) {
+ if (multiplicity == 1)
+ warning("relay port \"" + ep.toString() + "\" is not connected",
+ RoomPackage.eINSTANCE.getRoomClass_Name());
+ else
+ warning("relay port \"" + ep.toString() + "\" is only connected to "
+ + Multiplicities.toString(calculated) + " peers but its multiplicity is "
+ + Multiplicities.toString(multiplicity), RoomPackage.eINSTANCE.getRoomClass_Name());
+ }
+ });
+ }
+
+ /**
+ * Computes the multiplicity of an end point. If the end point is part of a
+ * replicated actor reference, the multiplicity of the end point is the product
+ * of the multiplicity of the port and the actor reference. Otherwise the
+ * multiplicity of the end point is just the multiplicity of the port.
+ *
+ * @param ep
+ * the end point to compute the multiplicity of
+ * @return the multiplicity of the end point
+ */
+ private int getMultiplicity(EndPoint ep) {
+ int multiplicator = ep.ref != null && ep.ref instanceof ActorRef ? ((ActorRef) ep.ref).getMultiplicity() : 1;
+ return Multiplicities.times(multiplicator, ep.port.getMultiplicity());
+ }
+
+ /**
+ * Collects all specified items of an actor class including inherited ones. This
+ * function terminates even if the class hierarchy is circular.
+ *
+ * @param <T>
+ * the type of the items
+ * @param ac
+ * the actor class to collect the items of
+ * @param getter
+ * a method reference to the getter of the items to be collected
+ * @return the collected items
+ */
+ private <T> Stream<T> getAll(ActorClass ac, Function<ActorClass, Collection<? extends T>> getter) {
+ return getClassHierarchy(ac).stream().flatMap(getter.andThen(Collection::stream));
+ }
+
+ /**
+ * Compute the class hierarchy ordering the classes from super classes to sub
+ * classes. This function terminates even if the class hierarchy is circular in
+ * contrast to {@link RoomHelpers#getClassHierarchy(ActorClass)}.
+ *
+ * @param ac
+ * the class to compute the hierarchy of
+ * @return the class hierarchy
+ */
+ private Collection<ActorClass> getClassHierarchy(ActorClass ac) {
+ if (ac.getActorBase() == null)
+ return Collections.singletonList(ac);
+
+ ArrayDeque<ActorClass> result = new ArrayDeque<>();
+ do {
+ result.addFirst(ac);
+ ac = ac.getActorBase();
+ } while (ac != null && !result.contains(ac));
+ return result;
+ }
+
+ /**
+ * This class allows to use end points as keys in a {@link Map}.
+ *
+ * @see BindingEndPoint
+ */
+ private static class EndPoint {
+ public final ActorContainerRef ref; // nullable
+ public final Port port;
+
+ public EndPoint(Port port) {
+ this.ref = null;
+ this.port = port;
+ }
+
+ public EndPoint(ActorContainerRef ref, Port port) {
+ this.ref = ref;
+ this.port = port;
+ }
+
+ public EndPoint(BindingEndPoint ep) {
+ this(ep.getActorRef(), ep.getPort());
+ }
+
+ @Override
+ public String toString() {
+ return ref == null ? port.getName() : ref.getName() + "." + port.getName();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(port, ref);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ EndPoint other = (EndPoint) obj;
+ return Objects.equals(port, other.port) && Objects.equals(ref, other.ref);
+ }
+ }
+
+ @Override
+ public void register(EValidatorRegistrar registrar) {
+ // library validator is not registered for a specific language
+ }
+} \ No newline at end of file
diff --git a/tests/org.eclipse.etrice.core.room.tests/models/TestBindings.room b/tests/org.eclipse.etrice.core.room.tests/models/TestBindings.room
index ecd50f38c..d0c30f529 100644
--- a/tests/org.eclipse.etrice.core.room.tests/models/TestBindings.room
+++ b/tests/org.eclipse.etrice.core.room.tests/models/TestBindings.room
@@ -83,21 +83,6 @@ RoomModel TestBindings {
}
}
- ActorClass Example8 {
- Structure {
- ActorRef ref1: AC5a
- ActorRef ref2: AC6a
- ActorRef ref3: AC5a
- ActorRef ref4: AC6a
-
- // a replicated port must have at most one replicated peer
- Binding ref1.p and ref2.pc
- Binding ref2.pc and ref3.p
- Binding ref3.p and ref4.pc
- }
- }
-
-
// helper actor classes
ActorClass AC1 {
diff --git a/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestBindings.java b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestBindings.java
index 3eb7ea63e..2d4a2fde2 100644
--- a/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestBindings.java
+++ b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestBindings.java
@@ -21,7 +21,6 @@ import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.etrice.core.room.ActorClass;
import org.eclipse.etrice.core.room.Binding;
import org.eclipse.etrice.core.room.RoomModel;
-import org.eclipse.etrice.core.validation.RoomJavaValidator;
import org.junit.Before;
import org.junit.Test;
@@ -80,13 +79,6 @@ public class TestBindings extends TestBase {
bind = getBinding("Example7", 0);
diag = getDiag(bind).getChildren().get(0);
assertEquals("derived protocols not connectable (both directions extended)", diag.getMessage());
-
- diag = getDiag(resource.getEObject("ActorClass:Example8"));
- assertEquals(2, diag.getChildren().size());
- Diagnostic childDiag = diag.getChildren().get(0);
- assertEquals(RoomJavaValidator.A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER, childDiag.getMessage());
- childDiag = diag.getChildren().get(1);
- assertEquals(RoomJavaValidator.A_REPLICATED_PORT_MUST_HAVE_AT_MOST_ONE_REPLICATED_PEER, childDiag.getMessage());
}
@Test
diff --git a/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestDynamicActors.java b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestDynamicActors.java
index da7b6950b..02741228a 100644
--- a/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestDynamicActors.java
+++ b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestDynamicActors.java
@@ -23,7 +23,7 @@ import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.etrice.core.room.ActorClass;
import org.eclipse.etrice.core.room.ActorRef;
import org.eclipse.etrice.core.room.ReferenceType;
-import org.eclipse.etrice.core.validation.RoomJavaValidator;
+import org.eclipse.etrice.core.validation.WiringValidator;
import org.junit.Before;
import org.junit.Test;
@@ -74,20 +74,17 @@ public class TestDynamicActors extends TestBase {
ar = (ActorRef) resource.getEObject("ActorRef:Test$dfltArrayAny");
errors = getDiag(ar).getChildren();
assertEquals("ActorRef dfltArrayAny has one error", 1, errors.size());
- assertEquals("ActorRef dfltArrayAny has error: "+RoomJavaValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
- RoomJavaValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
+ assertEquals("ActorRef dfltArrayAny has error: "+WiringValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
+ WiringValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
errors.get(0).getMessage());
ar = (ActorRef) resource.getEObject("ActorRef:Test$fixArrayAny");
errors = getDiag(ar).getChildren();
assertEquals("ActorRef fixArrayAny has one error", 1, errors.size());
- assertEquals("ActorRef fixArrayAny has error: "+RoomJavaValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
- RoomJavaValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
+ assertEquals("ActorRef fixArrayAny has error: "+WiringValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
+ WiringValidator.MULTIPLICITY_ANY_REQUIRES_OPTIONAL,
errors.get(0).getMessage());
ar = (ActorRef) resource.getEObject("ActorRef:Test$optArrayFixed");
errors = getDiag(ar).getChildren();
- assertEquals("ActorRef optArrayFixed has one error", 1, errors.size());
- assertEquals("ActorRef optArrayFixed has error: "+RoomJavaValidator.OPTIONAL_REFS_HAVE_TO_HAVE_MULTIPLICITY_ANY,
- RoomJavaValidator.OPTIONAL_REFS_HAVE_TO_HAVE_MULTIPLICITY_ANY,
- errors.get(0).getMessage());
+ assertTrue("ActorRef optArrayFixed has no errors", errors.isEmpty());
}
}
diff --git a/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestMultiplicities.java b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestMultiplicities.java
new file mode 100644
index 000000000..d68e43070
--- /dev/null
+++ b/tests/org.eclipse.etrice.core.room.tests/src/org/eclipse/etrice/core/TestMultiplicities.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2020 protos software gmbh (http://www.protos.de).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * CONTRIBUTORS:
+ * Jan Belle (initial contribution)
+ *
+ *******************************************************************************/
+
+package org.eclipse.etrice.core;
+
+import static org.eclipse.etrice.core.room.util.Multiplicities.compare;
+import static org.eclipse.etrice.core.room.util.Multiplicities.minimum;
+import static org.eclipse.etrice.core.room.util.Multiplicities.minus;
+import static org.eclipse.etrice.core.room.util.Multiplicities.plus;
+import static org.eclipse.etrice.core.room.util.Multiplicities.times;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class TestMultiplicities {
+ @Test
+ public void testCompare() {
+ assertTrue(compare(0, 0) == 0);
+ assertTrue(compare(1, 0) > 0);
+ assertTrue(compare(0, 1) < 0);
+ assertTrue(compare(1, 1) == 0);
+ assertTrue(compare(2, 1) > 0);
+ assertTrue(compare(1, 2) < 0);
+ assertTrue(compare(2, 2) == 0);
+ assertTrue(compare(100, 23) > 0);
+ assertTrue(compare(23, 100) < 0);
+ assertTrue(compare(-1, 0) > 0);
+ assertTrue(compare(0, -1) < 0);
+ assertTrue(compare(1, -1) < 0);
+ assertTrue(compare(-1, 1) > 0);
+ assertTrue(compare(-1, 2) > 0);
+ assertTrue(compare(2, -1) < 0);
+ assertTrue(compare(-1, -1) == 0);
+ }
+
+ @Test
+ public void testPlus() {
+ assertEquals(0, plus(0, 0));
+ assertEquals(1, plus(1, 0));
+ assertEquals(1, plus(0, 1));
+ assertEquals(2, plus(1, 1));
+ assertEquals(3, plus(2, 1));
+ assertEquals(3, plus(1, 2));
+ assertEquals(4, plus(2, 2));
+ assertEquals(123, plus(100, 23));
+ assertEquals(123, plus(23, 100));
+ assertEquals(-1, plus(-1, 0));
+ assertEquals(-1, plus(0, -1));
+ assertEquals(-1, plus(1, -1));
+ assertEquals(-1, plus(-1, 1));
+ assertEquals(-1, plus(-1, 2));
+ assertEquals(-1, plus(2, -1));
+ assertEquals(-1, plus(-1, -1));
+ }
+
+ @Test
+ public void testMinus() {
+ assertEquals(0, minus(0, 0));
+ assertEquals(1, minus(1, 0));
+ assertEquals(0, minus(1, 1));
+ assertEquals(1, minus(2, 1));
+ assertEquals(0, minus(2, 2));
+ assertEquals(77, minus(100, 23));
+ assertEquals(-1, minus(-1, 0));
+ assertEquals(-1, minus(-1, 1));
+ assertEquals(-1, minus(-1, 2));
+ assertEquals(-1, minus(-1, -1));
+ }
+
+ @Test
+ public void testTimes() {
+ assertEquals(0, times(0, 0));
+ assertEquals(0, times(1, 0));
+ assertEquals(0, times(0, 1));
+ assertEquals(1, times(1, 1));
+ assertEquals(2, times(2, 1));
+ assertEquals(2, times(1, 2));
+ assertEquals(4, times(2, 2));
+ assertEquals(2300, times(100, 23));
+ assertEquals(2300, times(23, 100));
+ assertEquals(0, times(-1, 0));
+ assertEquals(0, times(0, -1));
+ assertEquals(-1, times(1, -1));
+ assertEquals(-1, times(-1, 1));
+ assertEquals(-1, times(-1, 2));
+ assertEquals(-1, times(2, -1));
+ assertEquals(-1, times(-1, -1));
+ }
+
+ @Test
+ public void testMinimum() {
+ assertEquals(0, minimum(0, 0));
+ assertEquals(0, minimum(1, 0));
+ assertEquals(0, minimum(0, 1));
+ assertEquals(1, minimum(1, 1));
+ assertEquals(1, minimum(2, 1));
+ assertEquals(1, minimum(1, 2));
+ assertEquals(2, minimum(2, 2));
+ assertEquals(23, minimum(100, 23));
+ assertEquals(23, minimum(23, 100));
+ assertEquals(0, minimum(-1, 0));
+ assertEquals(0, minimum(0, -1));
+ assertEquals(1, minimum(1, -1));
+ assertEquals(1, minimum(-1, 1));
+ assertEquals(2, minimum(-1, 2));
+ assertEquals(2, minimum(2, -1));
+ assertEquals(-1, minimum(-1, -1));
+ }
+}

Back to the top