diff --git a/plugins/com.github.tno.pokayoke.activitysynthesis/src/com/github/tno/pokayoke/transform/activitysynthesis/AbstractActivityDependencyOrderer.java b/plugins/com.github.tno.pokayoke.activitysynthesis/src/com/github/tno/pokayoke/transform/activitysynthesis/AbstractActivityDependencyOrderer.java index e025fbeb0..9bb29e4dc 100644 --- a/plugins/com.github.tno.pokayoke.activitysynthesis/src/com/github/tno/pokayoke/transform/activitysynthesis/AbstractActivityDependencyOrderer.java +++ b/plugins/com.github.tno.pokayoke.activitysynthesis/src/com/github/tno/pokayoke/transform/activitysynthesis/AbstractActivityDependencyOrderer.java @@ -24,7 +24,7 @@ import org.eclipse.uml2.uml.Interval; import org.eclipse.uml2.uml.IntervalConstraint; -import com.github.tno.synthml.uml.profile.cif.CifContext; +import com.github.tno.synthml.uml.profile.util.PokaYokeUmlProfileUtil; /** * A dependency orderer for abstract activities, which determines the order in which abstract activities should be @@ -113,7 +113,7 @@ protected Set findDirectDependencies(Activity activity) { * otherwise. */ private boolean isBlockingOccurrenceConstraint(Constraint constraint) { - if (CifContext.isOccurrenceConstraint(constraint)) { + if (PokaYokeUmlProfileUtil.isContainedAsActivityOccurrenceConstraint(constraint)) { IntervalConstraint intervalConstraint = (IntervalConstraint)constraint; Interval interval = (Interval)intervalConstraint.getSpecification(); return interval.getMax().integerValue() <= 0; diff --git a/plugins/com.github.tno.pokayoke.transform.flatten/src/com/github/tno/pokayoke/transform/flatten/FlattenUMLActivity.java b/plugins/com.github.tno.pokayoke.transform.flatten/src/com/github/tno/pokayoke/transform/flatten/FlattenUMLActivity.java index ad2d01b8c..ba638527e 100644 --- a/plugins/com.github.tno.pokayoke.transform.flatten/src/com/github/tno/pokayoke/transform/flatten/FlattenUMLActivity.java +++ b/plugins/com.github.tno.pokayoke.transform.flatten/src/com/github/tno/pokayoke/transform/flatten/FlattenUMLActivity.java @@ -168,7 +168,7 @@ private void transformActivity(Activity childBehavior, CallBehaviorAction callBe // If the activity has any usage preconditions, conjunct them with the incoming guard of the // outgoing edge. Set activityPreconditions = childBehaviorCopy.getPreconditions().stream() - .filter(p -> PokaYokeUmlProfileUtil.isUsagePrecondition(p)) + .filter(p -> PokaYokeUmlProfileUtil.isUsagePreconditionConstraint(p)) .map(up -> PokaYokeUmlProfileUtil.getConstraintBodyExpression(up)) .collect(Collectors.toCollection(LinkedHashSet::new)); activityPreconditions diff --git a/plugins/com.github.tno.pokayoke.transform.uml2cameo/src/com/github/tno/pokayoke/transform/uml2cameo/UMLToCameoTransformer.java b/plugins/com.github.tno.pokayoke.transform.uml2cameo/src/com/github/tno/pokayoke/transform/uml2cameo/UMLToCameoTransformer.java index e9955f0aa..6dd81f388 100644 --- a/plugins/com.github.tno.pokayoke.transform.uml2cameo/src/com/github/tno/pokayoke/transform/uml2cameo/UMLToCameoTransformer.java +++ b/plugins/com.github.tno.pokayoke.transform.uml2cameo/src/com/github/tno/pokayoke/transform/uml2cameo/UMLToCameoTransformer.java @@ -134,7 +134,8 @@ public void transformModel() throws CoreException { CifContext cifContext = ctxManager.getGlobalContext(); - Preconditions.checkArgument(!cifContext.hasConstraints(c -> !CifContext.isPrimitiveTypeConstraint(c)), + Preconditions.checkArgument( + !cifContext.hasConstraints(c -> !PokaYokeUmlProfileUtil.isPrimitiveTypeConstraint(c)), "Only type constraints are supported."); Preconditions.checkArgument(!cifContext.hasAbstractActivities(), "Abstract activities are unsupported."); diff --git a/plugins/com.github.tno.pokayoke.transform.uml2cif/src/com/github/tno/pokayoke/transform/uml2cif/UmlToCifTranslator.java b/plugins/com.github.tno.pokayoke.transform.uml2cif/src/com/github/tno/pokayoke/transform/uml2cif/UmlToCifTranslator.java index e7aba0374..a229032b5 100644 --- a/plugins/com.github.tno.pokayoke.transform.uml2cif/src/com/github/tno/pokayoke/transform/uml2cif/UmlToCifTranslator.java +++ b/plugins/com.github.tno.pokayoke.transform.uml2cif/src/com/github/tno/pokayoke/transform/uml2cif/UmlToCifTranslator.java @@ -791,7 +791,7 @@ private void addActivityPrePostConditionsToEdgeGuards(BiMap eventEd List preOrPostConditions; if (addPreconditions) { preOrPostConditions = node.getActivity().getPreconditions().stream() - .filter(p -> PokaYokeUmlProfileUtil.isUsagePrecondition(p)).toList(); + .filter(p -> PokaYokeUmlProfileUtil.isUsagePreconditionConstraint(p)).toList(); } else { preOrPostConditions = node.getActivity().getPostconditions(); } @@ -1383,7 +1383,7 @@ private Automaton createIntervalAutomaton(String name, List events, int m private Pair, AlgVariable> translatePreconditions() { // Translate the user-specified synthesis preconditions of the activity. List synthesisPreconditions = activity.getPreconditions().stream() - .filter(p -> PokaYokeUmlProfileUtil.isSynthesisPrecondition(p)).toList(); + .filter(p -> PokaYokeUmlProfileUtil.isSynthesisPreconditionConstraint(p)).toList(); List preconditionVars = translateUserSpecifiedPrePostconditions(synthesisPreconditions); // Add the synthesized activity's initial node configuration. @@ -1649,17 +1649,26 @@ private List createDisableEventsWhenDoneRequirements() { } /** - * Translates all UML class constraints that are in context to CIF requirement invariants. + * Translates all UML class constraints that are in context, as well as all activity's constraints, to CIF + * requirement invariants. * * @return The translated CIF requirement invariants. */ private List translateRequirements() { List cifInvariants = new ArrayList<>(); + // Translate class requirements. for (Constraint umlConstraint: activity.getContext().getOwnedRules()) { cifInvariants.addAll(translateRequirement(umlConstraint)); } + // Translate activity requirements. + for (Constraint umlConstraint: activity.getOwnedRules()) { + if (PokaYokeUmlProfileUtil.isRequirementConstraint(umlConstraint)) { + cifInvariants.addAll(translateRequirement(umlConstraint)); + } + } + return cifInvariants; } diff --git a/plugins/com.github.tno.pokayoke.transform.uml2gal/src/com/github/tno/pokayoke/transform/uml2gal/Uml2GalTranslator.java b/plugins/com.github.tno.pokayoke.transform.uml2gal/src/com/github/tno/pokayoke/transform/uml2gal/Uml2GalTranslator.java index f59982f8e..6165b03ab 100644 --- a/plugins/com.github.tno.pokayoke.transform.uml2gal/src/com/github/tno/pokayoke/transform/uml2gal/Uml2GalTranslator.java +++ b/plugins/com.github.tno.pokayoke.transform.uml2gal/src/com/github/tno/pokayoke/transform/uml2gal/Uml2GalTranslator.java @@ -149,7 +149,8 @@ public Specification translate(Model model) throws CoreException { // Check transformation preconditions. Preconditions.checkArgument(!cifContext.hasOpaqueBehaviors(), "Opaque behaviors are unsupported."); - Preconditions.checkArgument(!cifContext.hasConstraints(c -> !CifContext.isPrimitiveTypeConstraint(c)), + Preconditions.checkArgument( + !cifContext.hasConstraints(c -> !PokaYokeUmlProfileUtil.isPrimitiveTypeConstraint(c)), "Only type constraints are supported."); Preconditions.checkArgument(!cifContext.hasAbstractActivities(), "Abstract activities are unsupported."); Preconditions.checkArgument(!cifContext.hasParameterizedActivities(), diff --git a/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/cif/CifContext.java b/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/cif/CifContext.java index 729c946ef..1ad3116a2 100644 --- a/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/cif/CifContext.java +++ b/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/cif/CifContext.java @@ -16,7 +16,6 @@ import java.util.function.Predicate; import org.eclipse.uml2.uml.Activity; -import org.eclipse.uml2.uml.Behavior; import org.eclipse.uml2.uml.Class; import org.eclipse.uml2.uml.Constraint; import org.eclipse.uml2.uml.ControlFlow; @@ -24,11 +23,9 @@ import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Enumeration; import org.eclipse.uml2.uml.EnumerationLiteral; -import org.eclipse.uml2.uml.IntervalConstraint; import org.eclipse.uml2.uml.Model; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.OpaqueBehavior; -import org.eclipse.uml2.uml.PrimitiveType; import org.eclipse.uml2.uml.Property; import com.github.tno.synthml.uml.profile.util.PokaYokeTypeUtil; @@ -252,30 +249,6 @@ default boolean hasConstraints(Predicate predicate) { return getDeclaredElements().stream().anyMatch(e -> e instanceof Constraint c && predicate.test(c)); } - public static boolean isActivityPrePostconditionConstraint(Constraint constraint) { - return isActivityPreconditionConstraint(constraint) || isActivityPostconditionConstraint(constraint); - } - - public static boolean isActivityPreconditionConstraint(Constraint constraint) { - return constraint.getContext() instanceof Activity a && a.getPreconditions().contains(constraint); - } - - public static boolean isActivityPostconditionConstraint(Constraint constraint) { - return constraint.getContext() instanceof Activity a && a.getPostconditions().contains(constraint); - } - - public static boolean isClassConstraint(Constraint constraint) { - return constraint.getContext() instanceof Class clazz && !(clazz instanceof Behavior); - } - - public static boolean isOccurrenceConstraint(Constraint constraint) { - return constraint.getContext() instanceof Activity && constraint instanceof IntervalConstraint; - } - - public static boolean isPrimitiveTypeConstraint(Constraint constraint) { - return constraint.getContext() instanceof PrimitiveType; - } - default boolean hasAbstractActivities() { return getDeclaredElements().stream().anyMatch(e -> e instanceof Activity a && a.isAbstract()); } diff --git a/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/util/PokaYokeUmlProfileUtil.java b/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/util/PokaYokeUmlProfileUtil.java index d740993cb..4a768aa48 100644 --- a/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/util/PokaYokeUmlProfileUtil.java +++ b/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/util/PokaYokeUmlProfileUtil.java @@ -37,6 +37,7 @@ import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.OpaqueExpression; import org.eclipse.uml2.uml.Package; +import org.eclipse.uml2.uml.PrimitiveType; import org.eclipse.uml2.uml.Profile; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.RedefinableElement; @@ -82,7 +83,7 @@ public class PokaYokeUmlProfileUtil { private static final String ST_FORMAL_CONSTRAINT = SynthMLPackage.Literals.FORMAL_CONSTRAINT.getName(); - public static final String ST_CLASS_REQUIREMENT = SynthMLPackage.Literals.REQUIREMENT.getName(); + public static final String ST_REQUIREMENT = SynthMLPackage.Literals.REQUIREMENT.getName(); public static final String ST_SYNTHESIS_PRECONDITION = SynthMLPackage.Literals.SYNTHESIS_PRECONDITION.getName(); @@ -112,8 +113,7 @@ public class PokaYokeUmlProfileUtil { + ST_FORMAL_CONSTRAINT; /** Qualified name for the {@link Requirement} stereotype. */ - public static final String REQUIREMENT_STEREOTYPE = POKA_YOKE_PROFILE + NamedElement.SEPARATOR - + ST_CLASS_REQUIREMENT; + public static final String REQUIREMENT_STEREOTYPE = POKA_YOKE_PROFILE + NamedElement.SEPARATOR + ST_REQUIREMENT; /** Qualified name for the {@link SynthesisPrecondition} stereotype. */ public static final String SYNTHESIS_PRECONDITION_STEREOTYPE = POKA_YOKE_PROFILE + NamedElement.SEPARATOR @@ -523,13 +523,13 @@ public static Stereotype getConstraintFirstStereotype(Constraint constraint) { * @return A list of supported stereotypes for the given constraint. */ public static List getSupportedConstraintStereotypes(Constraint constraint) { - if (isPreconditionConstraint(constraint)) { + if (isContainedAsActivityPrecondition(constraint)) { return List.of(getStereotype(constraint, ST_SYNTHESIS_PRECONDITION), getStereotype(constraint, ST_USAGE_PRECONDITION)); - } else if (isPostconditionConstraint(constraint)) { + } else if (isContainedAsActivityPostcondition(constraint)) { return List.of(getStereotype(constraint, ST_POSTCONDITION)); - } else if (isClassRequirement(constraint)) { - return List.of(getStereotype(constraint, ST_CLASS_REQUIREMENT)); + } else if (isContainedAsClassOrActivityOwnedRule(constraint)) { + return List.of(getStereotype(constraint, ST_REQUIREMENT)); } else { return List.of(); } @@ -539,12 +539,30 @@ private static Stereotype getStereotype(Constraint constraint, String stereotype return getPokaYokeProfile(constraint).getOwnedStereotype(stereotypeName); } - private static boolean isPreconditionConstraint(Constraint constraint) { + public static boolean isContainedAsActivityPrecondition(Constraint constraint) { return (constraint.eContainer() instanceof Activity activity) && activity.getPreconditions().contains(constraint); } - public static boolean isSynthesisPrecondition(Constraint constraint) { + public static boolean isContainedAsActivityPostcondition(Constraint constraint) { + return (constraint.eContainer() instanceof Activity activity) + && activity.getPostconditions().contains(constraint); + } + + public static boolean isContainedAsClassOrActivityOwnedRule(Constraint constraint) { + // Activity is a sub-type of Classifier. + return constraint.eContainer() instanceof Classifier clazz && clazz.getOwnedRules().contains(constraint); + } + + public static boolean isContainedAsActivityOccurrenceConstraint(Constraint constraint) { + return constraint.getContext() instanceof Activity && constraint instanceof IntervalConstraint; + } + + public static boolean isPrimitiveTypeConstraint(Constraint constraint) { + return constraint.getContext() instanceof PrimitiveType; + } + + public static boolean isSynthesisPreconditionConstraint(Constraint constraint) { List appliedStereotypes = constraint.getAppliedStereotypes(); if (appliedStereotypes.isEmpty()) { @@ -554,7 +572,7 @@ public static boolean isSynthesisPrecondition(Constraint constraint) { return appliedStereotypes.get(0).getName().equals(ST_SYNTHESIS_PRECONDITION); } - public static boolean isUsagePrecondition(Constraint constraint) { + public static boolean isUsagePreconditionConstraint(Constraint constraint) { List appliedStereotypes = constraint.getAppliedStereotypes(); if (appliedStereotypes.isEmpty()) { @@ -564,13 +582,14 @@ public static boolean isUsagePrecondition(Constraint constraint) { return appliedStereotypes.get(0).getName().equals(ST_USAGE_PRECONDITION); } - private static boolean isPostconditionConstraint(Constraint constraint) { - return (constraint.eContainer() instanceof Activity activity) - && activity.getPostconditions().contains(constraint); - } + public static boolean isRequirementConstraint(Constraint constraint) { + List appliedStereotypes = constraint.getAppliedStereotypes(); + + if (appliedStereotypes.isEmpty()) { + return false; + } - private static boolean isClassRequirement(Constraint constraint) { - return (constraint.eContainer() instanceof Classifier clazz) && clazz.getOwnedRules().contains(constraint); + return appliedStereotypes.get(0).getName().equals(ST_REQUIREMENT); } /** @@ -592,7 +611,7 @@ public static void setConstraintStereotype(Constraint constraint, Stereotype ste } private static String getQualifiedStereotypeName(String stereotypeName) { - if (ST_CLASS_REQUIREMENT.equals(stereotypeName)) { + if (ST_REQUIREMENT.equals(stereotypeName)) { return REQUIREMENT_STEREOTYPE; } else if (ST_SYNTHESIS_PRECONDITION.equals(stereotypeName)) { return SYNTHESIS_PRECONDITION_STEREOTYPE; @@ -616,7 +635,7 @@ public static void setConstraintExpression(Constraint constraint, String newValu public static String getStereotypeName(Stereotype st) { // Returns a slightly better formatted name for the preconditions. - if (st.getName().equals(ST_CLASS_REQUIREMENT)) { + if (st.getName().equals(ST_REQUIREMENT)) { return "Requirement"; } else if (st.getName().equals(ST_SYNTHESIS_PRECONDITION)) { return "Synthesis precondition"; diff --git a/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/validation/PokaYokeProfileValidator.java b/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/validation/PokaYokeProfileValidator.java index 8761f5afb..4a31c1ac8 100644 --- a/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/validation/PokaYokeProfileValidator.java +++ b/plugins/com.github.tno.synthml.uml.profile.util/src/com/github/tno/synthml/uml/profile/validation/PokaYokeProfileValidator.java @@ -22,6 +22,7 @@ import java.util.Stack; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.escet.cif.parser.ast.AInvariant; import org.eclipse.escet.cif.parser.ast.automata.AAssignmentUpdate; @@ -228,8 +229,8 @@ private void checkGlobalUniqueNames(Model model) { .getReferenceableElementsInclDuplicates(); for (Map.Entry> entry: referenceableElementsInclDuplicates.entrySet()) { // Skip primitive type constraints, that always have the same fixed name. - if (entry.getValue().stream() - .allMatch(t -> t instanceof Constraint constr && CifContext.isPrimitiveTypeConstraint(constr))) + if (entry.getValue().stream().allMatch( + t -> t instanceof Constraint constr && PokaYokeUmlProfileUtil.isPrimitiveTypeConstraint(constr))) { continue; } @@ -544,8 +545,15 @@ private void checkValidActivity(Activity activity) { .filter(IntervalConstraint.class::isInstance).map(IntervalConstraint.class::cast) .collect(Collectors.toCollection(LinkedHashSet::new)); - if (!members.equals(Sets.union(preAndPostconditions, intervalConstraints))) { - error("Activity should contain only precondition, postcondition, and interval constraint members.", + Set activityRequirements = activity.getOwnedRules().stream() + .filter(r -> PokaYokeUmlProfileUtil.isRequirementConstraint(r)).map(Constraint.class::cast) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + Set allowedMembers = Stream.of(preAndPostconditions, intervalConstraints, activityRequirements) + .flatMap(Set::stream).collect(Collectors.toSet()); + + if (!members.equals(allowedMembers)) { + error("Activity should contain only precondition, postcondition, constraints and interval constraint members.", UMLPackage.Literals.NAMESPACE__MEMBER); } @@ -1001,14 +1009,21 @@ private void checkValidOpaqueBehavior(OpaqueBehavior behavior) { private void checkValidConstraint(Constraint constraint) { checkNamingConventions(constraint, NamingConvention.IDENTIFIER); - if (CifContext.isActivityPrePostconditionConstraint(constraint)) { + if (PokaYokeUmlProfileUtil.isContainedAsActivityPrecondition(constraint) + || PokaYokeUmlProfileUtil.isContainedAsActivityPostcondition(constraint)) + { checkValidActivityPrePostconditionConstraint(constraint); - } else if (CifContext.isClassConstraint(constraint)) { - checkValidClassConstraint(constraint); - } else if (CifContext.isOccurrenceConstraint(constraint)) { - checkValidOccurrenceConstraint((IntervalConstraint)constraint); - } else if (CifContext.isPrimitiveTypeConstraint(constraint)) { - // The constraints for primitive types are validated in #checkValidPrimitiveType(PrimitiveType) + } else if (PokaYokeUmlProfileUtil.isContainedAsClassOrActivityOwnedRule(constraint)) { + if (PokaYokeUmlProfileUtil.isPrimitiveTypeConstraint(constraint)) { + // The constraints for primitive types are validated in #checkValidPrimitiveType(PrimitiveType). + } else if (PokaYokeUmlProfileUtil.isContainedAsActivityOccurrenceConstraint(constraint)) { + checkValidOccurrenceConstraint((IntervalConstraint)constraint); + } else if (PokaYokeUmlProfileUtil.isRequirementConstraint(constraint)) { + // Check the class and activity requirements. + checkValidRequirementConstraint(constraint); + } else { + error("Unsupported constraint", UMLPackage.Literals.CONSTRAINT__CONTEXT); + } } else { error("Unsupported constraint", UMLPackage.Literals.CONSTRAINT__CONTEXT); } @@ -1023,14 +1038,14 @@ private void checkValidActivityPrePostconditionConstraint(Constraint constraint) return; } - if (CifContext.isActivityPreconditionConstraint(constraint) + if (PokaYokeUmlProfileUtil.isContainedAsActivityPrecondition(constraint) && !(stereotypes.get(0).getName().equals(PokaYokeUmlProfileUtil.ST_SYNTHESIS_PRECONDITION) || stereotypes.get(0).getName().equals(PokaYokeUmlProfileUtil.ST_USAGE_PRECONDITION))) { error(String.format("Constraint '%s' must have a precondition stereotype applied.", constraint.getName()), UMLPackage.Literals.CONSTRAINT__SPECIFICATION); return; - } else if (CifContext.isActivityPostconditionConstraint(constraint) + } else if (PokaYokeUmlProfileUtil.isContainedAsActivityPostcondition(constraint) && !(stereotypes.get(0).getName().equals(PokaYokeUmlProfileUtil.ST_POSTCONDITION))) { error(String.format("Constraint '%s' must have a postcondition stereotype applied.", constraint.getName()), @@ -1058,7 +1073,7 @@ private void checkValidActivityPrePostconditionConstraint(Constraint constraint) } } - private void checkValidClassConstraint(Constraint constraint) { + private void checkValidRequirementConstraint(Constraint constraint) { // Check that the constraint has the right stereotype applied. List stereotypes = constraint.getAppliedStereotypes(); @@ -1068,7 +1083,7 @@ private void checkValidClassConstraint(Constraint constraint) { return; } - if (!stereotypes.get(0).getName().equals(PokaYokeUmlProfileUtil.ST_CLASS_REQUIREMENT)) { + if (!stereotypes.get(0).getName().equals(PokaYokeUmlProfileUtil.ST_REQUIREMENT)) { error(String.format("Constraint '%s' must have a requirement stereotype applied.", constraint.getName()), UMLPackage.Literals.CONSTRAINT__SPECIFICATION); return; @@ -1080,7 +1095,7 @@ private void checkValidClassConstraint(Constraint constraint) { } try { - new CifTypeChecker(getGlobalContext(constraint)).checkInvariant(CifParserHelper.parseInvariant(constraint)); + new CifTypeChecker(getScopedContext(constraint)).checkInvariant(CifParserHelper.parseInvariant(constraint)); } catch (RuntimeException e) { error("Invalid invariant: " + e.getLocalizedMessage(), UMLPackage.Literals.CONSTRAINT__SPECIFICATION); }