Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
12cb8f6
#683 CifContext: add `isActivityRequirement` method
AndreaPuffo Feb 10, 2026
58705a8
#683 PokaYokeProfileValidator: allow activity requirement constraints
AndreaPuffo Feb 10, 2026
6fe4ef6
#683 PokaYokeProfileValidator: rename method
AndreaPuffo Feb 10, 2026
212a07b
#683 PokaYokeUmlProfileUtil, PokaYokeProfileValidator: rename stereotype
AndreaPuffo Feb 11, 2026
3bb41f9
#683 UmlToCifTranslator: update JavaDoc.
AndreaPuffo Feb 25, 2026
0fa177a
#683 CifContext: move method `isActivityRequirement`.
AndreaPuffo Feb 25, 2026
a468205
#683 CifContext, UmlToCifTranslator, PYProfileValidator: rename method
AndreaPuffo Feb 25, 2026
19fa69f
#683 CifContext: remove `isActivityRequirementConstraint`
AndreaPuffo Feb 25, 2026
6b5dc4d
#683 PokaYokeUmlProfileUtil: add `isRequirementConstraint` method
AndreaPuffo Feb 25, 2026
8a1f8c1
#683 PokaYokeProfileValidator, UmlToCifTranslator: use new method
AndreaPuffo Feb 25, 2026
ee430c6
#683 PokaYokeUmlProfileUtil: rename method.
AndreaPuffo Feb 25, 2026
3127da1
#683 PokaYokeProfileValidator: create set union before check.
AndreaPuffo Feb 25, 2026
5aa521f
#683 PokaYokeProfileValidator: rename method.
AndreaPuffo Feb 25, 2026
ab173a0
#683 PokaYokeProfileValidator: use scoped context.
AndreaPuffo Feb 25, 2026
549e163
#683 Apply suggestions from code review
AndreaPuffo Mar 9, 2026
8cd8aac
#683 PokaYokeProfileValidator: change variable name after review
AndreaPuffo Mar 9, 2026
f689a77
#683 PokaYokeUmlProfileUtil: move methods.
AndreaPuffo Mar 9, 2026
686be24
#683 PokaYokeUmlProfileUtil: rename methods.
AndreaPuffo Mar 9, 2026
281f185
#683 PokaYokeUmlProfileUtil: rename methods
AndreaPuffo Mar 9, 2026
1c52cc8
#683 PokaYokeUmlProfileUtil:
AndreaPuffo Mar 9, 2026
49a6a8e
#683 CifContext: remove `isPrimitiveTypeConstraint`
AndreaPuffo Mar 9, 2026
3312f5d
#683 CifContext: remove `isOccurrenceConstraint`
AndreaPuffo Mar 9, 2026
e623614
#683 CifContext: remove method to check activity pre/post condition
AndreaPuffo Mar 9, 2026
0487e95
#638 PokaYokeProfileValidator: update if-clause with new methods
AndreaPuffo Mar 10, 2026
2f63429
#638 PokaYokeUmlProfileUtil: add comment
AndreaPuffo Mar 10, 2026
f0ba3f0
#638 UmlToCifTranslator: reformat file
AndreaPuffo Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -113,7 +113,7 @@ protected Set<Activity> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ private void addActivityPrePostConditionsToEdgeGuards(BiMap<Event, Edge> eventEd
List<Constraint> 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();
}
Expand Down Expand Up @@ -1383,7 +1383,7 @@ private Automaton createIntervalAutomaton(String name, List<Event> events, int m
private Pair<List<AlgVariable>, AlgVariable> translatePreconditions() {
// Translate the user-specified synthesis preconditions of the activity.
List<Constraint> synthesisPreconditions = activity.getPreconditions().stream()
.filter(p -> PokaYokeUmlProfileUtil.isSynthesisPrecondition(p)).toList();
.filter(p -> PokaYokeUmlProfileUtil.isSynthesisPreconditionConstraint(p)).toList();
List<AlgVariable> preconditionVars = translateUserSpecifiedPrePostconditions(synthesisPreconditions);

// Add the synthesized activity's initial node configuration.
Expand Down Expand Up @@ -1649,17 +1649,26 @@ private List<Invariant> 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<Invariant> translateRequirements() {
List<Invariant> cifInvariants = new ArrayList<>();

// Translate class requirements.
for (Constraint umlConstraint: activity.getContext().getOwnedRules()) {
cifInvariants.addAll(translateRequirement(umlConstraint));
}

// Translate activity requirements.
for (Constraint umlConstraint: activity.getOwnedRules()) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it sufficient to translate the requirements like this? Should we also include all requirements of other activities that we call, etc? (all other 'relevant' activities)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this to be consistent with the occurrence constraints: these are considered only during the activity synthesis, not when an activity is called. Better to have a conversation offline about this.

if (PokaYokeUmlProfileUtil.isRequirementConstraint(umlConstraint)) {
cifInvariants.addAll(translateRequirement(umlConstraint));
}
}

return cifInvariants;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,16 @@
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;
import org.eclipse.uml2.uml.DataType;
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;
Expand Down Expand Up @@ -252,30 +249,6 @@ default boolean hasConstraints(Predicate<Constraint> 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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -523,13 +523,13 @@ public static Stereotype getConstraintFirstStereotype(Constraint constraint) {
* @return A list of supported stereotypes for the given constraint.
*/
public static List<Stereotype> 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();
}
Expand All @@ -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<Stereotype> appliedStereotypes = constraint.getAppliedStereotypes();

if (appliedStereotypes.isEmpty()) {
Expand All @@ -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<Stereotype> appliedStereotypes = constraint.getAppliedStereotypes();

if (appliedStereotypes.isEmpty()) {
Expand All @@ -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) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move isPostconditionConstraint and isClassRequirement to just above isSynthesisPrecondition, to have methods with similar implementation close together?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are three methods for 'where' the constraint is (precondition/postcondition of activity, rule of a class/activity), and three methods about what kind (synthesis pre, usage pre, requirement).
I'm thinking the names could be clearer. Maybe:

  • isSynthesisPreconditionConstraint/isUsagePreconditionConstraint/isRequirementConstraint to just check the stereotype that is applied, more or less as we have now (but all end their name with Constraint).
  • isPreconditionConstraint/isPostconditionConstraint/isClassRequirementConstraint -> isContainedAsActivityPrecondition/isContainedAsActivityPostcondition/isContainedAsClassOrActivityOwnedRule to see where they are contained in the model. That distinguishes their names better.

List<Stereotype> 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);
}

/**
Expand All @@ -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;
Expand All @@ -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";
Expand Down
Loading