Skip to content

Commit

Permalink
Add option to not create a repetitionCounter variable for repetition …
Browse files Browse the repository at this point in the history
…elements in CMMN
  • Loading branch information
tijsrademakers committed Nov 17, 2023
1 parent 2e209bf commit f4f003c
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ public interface CmmnXmlConstants {
String ATTRIBUTE_TASK_COMPLETER_VARIABLE_NAME = "taskCompleterVariableName";

String ATTRIBUTE_REPETITION_COUNTER_VARIABLE_NAME = "counterVariable";
String ATTRIBUTE_IGNORE_REPETITION_COUNTER_VARIABLE = "ignoreCounterVariable";
String ATTRIBUTE_REPETITION_MAX_INSTANCE_COUNT_NAME = "maxInstanceCount";
String ATTRIBUTE_REPETITION_COLLECTION_VARIABLE_NAME = "collectionVariable";
String ATTRIBUTE_REPETITION_ELEMENT_VARIABLE_NAME = "elementVariable";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ protected CmmnElement convert(XMLStreamReader xtr, ConversionHelper conversionHe

repetitionRule.setRepetitionCounterVariableName(xtr.getAttributeValue(CmmnXmlConstants.FLOWABLE_EXTENSIONS_NAMESPACE,
CmmnXmlConstants.ATTRIBUTE_REPETITION_COUNTER_VARIABLE_NAME));


String ignoreRepetitionCounterVariableValue = xtr.getAttributeValue(CmmnXmlConstants.FLOWABLE_EXTENSIONS_NAMESPACE, CmmnXmlConstants.ATTRIBUTE_IGNORE_REPETITION_COUNTER_VARIABLE);
if ("true".equalsIgnoreCase(ignoreRepetitionCounterVariableValue)) {
repetitionRule.setIgnoreRepetitionCounterVariable(true);
}

String maxInstanceCountValue = xtr.getAttributeValue(CmmnXmlConstants.FLOWABLE_EXTENSIONS_NAMESPACE, CmmnXmlConstants.ATTRIBUTE_REPETITION_MAX_INSTANCE_COUNT_NAME);
if (maxInstanceCountValue == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ protected void internalExecute() {
planItemInstanceEntity.getParentVariableScope().setVariable(variableName, bpmnAggregation);
}
}
setRepetitionCounter(planItemInstanceEntity, repetitionCounter + 1);

if (repetitionRule.getAggregations() != null || !PlanItemInstanceUtil.hasIgnoreCounterVariable(planItemInstanceEntity)) {
setRepetitionCounter(planItemInstanceEntity, repetitionCounter + 1);
}
}

CmmnHistoryManager cmmnHistoryManager = CommandContextUtil.getCmmnHistoryManager(commandContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ public void run() {
planItemInstanceEntity.getParentVariableScope().setVariable(variableName, bpmnAggregation);
}
}
setRepetitionCounter(planItemInstanceEntity, repetitionCounter + 1);

if (repetitionRule.getAggregations() != null || !PlanItemInstanceUtil.hasIgnoreCounterVariable(planItemInstanceEntity)) {
setRepetitionCounter(planItemInstanceEntity, repetitionCounter + 1);
}
}

CmmnHistoryManager cmmnHistoryManager = CommandContextUtil.getCmmnHistoryManager(commandContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import org.flowable.cmmn.model.PlanItem;
import org.flowable.cmmn.model.PlanItemDefinition;
import org.flowable.cmmn.model.ProcessTask;
import org.flowable.cmmn.model.RepetitionRule;
import org.flowable.cmmn.model.Sentry;
import org.flowable.cmmn.model.SentryIfPart;
import org.flowable.cmmn.model.SentryOnPart;
Expand Down Expand Up @@ -1139,8 +1140,11 @@ protected PlanItemInstanceEntity copyAndInsertPlanItemInstance(CommandContext co
.create();

if (hasRepetitionRule(planItemInstanceEntityToCopy)) {
int counter = getRepetitionCounter(planItemInstanceEntityToCopy);
setRepetitionCounter(planItemInstanceEntity, counter);
RepetitionRule repetitionRule = planItemInstanceEntity.getPlanItem().getItemControl().getRepetitionRule();
if (repetitionRule.getAggregations() != null || !repetitionRule.isIgnoreRepetitionCounterVariable()) {
int counter = getRepetitionCounter(planItemInstanceEntityToCopy);
setRepetitionCounter(planItemInstanceEntity, counter);
}
}

return planItemInstanceEntity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ public static PlanItemInstanceEntity copyAndInsertPlanItemInstance(CommandContex
Map<String, Object> localVariables, boolean addToParent, boolean silentNameExpressionEvaluation) {

if (ExpressionUtil.hasRepetitionRule(planItemInstanceEntityToCopy)) {
int counter = getRepetitionCounter(planItemInstanceEntityToCopy);
if (localVariables == null) {
localVariables = new HashMap<>(0);
RepetitionRule repetitionRule = planItemInstanceEntityToCopy.getPlanItem().getItemControl().getRepetitionRule();

if (repetitionRule.getAggregations() != null || !repetitionRule.isIgnoreRepetitionCounterVariable()) {
int counter = getRepetitionCounter(planItemInstanceEntityToCopy);
if (localVariables == null) {
localVariables = new HashMap<>(0);
}

localVariables.put(getCounterVariable(planItemInstanceEntityToCopy), counter);
}
localVariables.put(getCounterVariable(planItemInstanceEntityToCopy), counter);
}

PlanItemInstance stagePlanItem = planItemInstanceEntityToCopy.getStagePlanItemInstanceEntity();
Expand Down Expand Up @@ -160,6 +165,10 @@ public static int getRepetitionCounter(PlanItemInstanceEntity repeatingPlanItemI
return counter.intValue();
}
}

public static boolean hasIgnoreCounterVariable(PlanItemInstanceEntity repeatingPlanItemInstanceEntity) {
return repeatingPlanItemInstanceEntity.getPlanItem().getItemControl().getRepetitionRule().isIgnoreRepetitionCounterVariable();
}

public static String getCounterVariable(PlanItemInstanceEntity repeatingPlanItemInstanceEntity) {
String repetitionCounterVariableName = repeatingPlanItemInstanceEntity.getPlanItem().getItemControl().getRepetitionRule().getRepetitionCounterVariableName();
Expand All @@ -184,7 +193,9 @@ protected static PlanItemInstanceEntity createPlanItemInstanceDuplicateForCollec
CommandContextUtil.getAgenda(commandContext).planCreateRepeatedPlanItemInstanceOperation(childPlanItemInstanceEntity);

// The repetition counter is 1 based
childPlanItemInstanceEntity.setVariableLocal(PlanItemInstanceUtil.getCounterVariable(childPlanItemInstanceEntity), index + 1);
if (repetitionRule.getAggregations() != null || !repetitionRule.isIgnoreRepetitionCounterVariable()) {
childPlanItemInstanceEntity.setVariableLocal(PlanItemInstanceUtil.getCounterVariable(childPlanItemInstanceEntity), index + 1);
}

// createPlanItemInstance operations will also sync planItemInstance history
CommandContextUtil.getAgenda(commandContext).planActivatePlanItemInstanceOperation(childPlanItemInstanceEntity, entryCriterionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ public void testPlanItemLocalVariablesWithCollection() {
.start();

List<Task> tasks = cmmnTaskService.createTaskQuery()
.caseInstanceId(caseInstance.getId())
.orderByTaskPriority().asc()
.list();

Expand Down Expand Up @@ -521,6 +522,43 @@ public void testPlanItemLocalVariablesWithCollection() {
entry("initiator", null)
);
}

@Test
@CmmnDeployment
public void testPlanItemLocalVariablesWithCollectionIgnoreCounter() {
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("repeatingTask")
.transientVariable("myCollection", Arrays.asList("one", "two", "three"))
.start();

List<Task> tasks = cmmnTaskService.createTaskQuery()
.caseInstanceId(caseInstance.getId())
.orderByTaskPriority().asc()
.list();

assertThat(tasks).hasSize(3);

assertThat(cmmnTaskService.getVariables(tasks.get(0).getId()))
.containsOnly(
entry("item", "one"),
entry("itemIndex", 0),
entry("initiator", null)
);

assertThat(cmmnTaskService.getVariables(tasks.get(1).getId()))
.containsOnly(
entry("item", "two"),
entry("itemIndex", 1),
entry("initiator", null)
);

assertThat(cmmnTaskService.getVariables(tasks.get(2).getId()))
.containsOnly(
entry("item", "three"),
entry("itemIndex", 2),
entry("initiator", null)
);
}

@Test
@CmmnDeployment
Expand Down Expand Up @@ -564,6 +602,33 @@ public void testPlanItemLocalVariables() {

assertCaseInstanceEnded(caseInstance);
}

@Test
@CmmnDeployment
public void testPlanItemLocalVariablesIgnoreCounter() {
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("repeatingTask")
.variable("initiator", "johndoe")
.start();

Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult();
assertThat(task).isNotNull();

assertThat(cmmnTaskService.getVariables(task.getId()))
.containsOnly(
entry("initiator", "johndoe")
);

cmmnTaskService.complete(task.getId());

task = cmmnTaskService.createTaskQuery().singleResult();
assertThat(task).isNotNull();

assertThat(cmmnTaskService.getVariables(task.getId()))
.containsOnly(
entry("initiator", "johndoe")
);
}

@Test
@CmmnDeployment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public void testSequentialRepeatingUserTask() {

Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskName("My Task").singleResult();

assertThat(cmmnTaskService.getVariable(task.getId(), "repetitionCounter")).isEqualTo(1);
Map<String, Object> variables = new HashMap<>();
variables.put("approved", false);
variables.put("description", "description task 0");
Expand Down Expand Up @@ -125,12 +126,14 @@ public void testSequentialRepeatingUserTask() {
}

task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskName("My Task").singleResult();
assertThat(cmmnTaskService.getVariable(task.getId(), "repetitionCounter")).isEqualTo(2);
variables.put("approved", true);
variables.put("description", "description task 1");
cmmnTaskService.setAssignee(task.getId(), "userTwo");
cmmnTaskService.complete(task.getId(), variables);

task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskName("My Task").singleResult();
assertThat(cmmnTaskService.getVariable(task.getId(), "repetitionCounter")).isEqualTo(3);
variables.put("approved", false);
variables.put("description", "description task 2");
cmmnTaskService.setAssignee(task.getId(), "userThree");
Expand Down Expand Up @@ -167,6 +170,57 @@ public void testSequentialRepeatingUserTask() {
}

}

@Test
@CmmnDeployment
public void testSequentialRepeatingUserTaskIgnoreCounter() {
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("repeatingTask")
.variable("nrOfLoops", 3)
.variable("otherVariable", "Hello World")
.start();

ArrayNode reviews = (ArrayNode) cmmnRuntimeService.getVariable(caseInstance.getId(), "reviews");

assertThatJson(reviews)
.isEqualTo("["
+ "{ userId: null }"
+ "]");

Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskName("My Task").singleResult();

assertThat(cmmnTaskService.getVariable(task.getId(), "repetitionCounter")).isEqualTo(1);
Map<String, Object> variables = new HashMap<>();
variables.put("approved", false);
variables.put("description", "description task 0");
cmmnTaskService.setAssignee(task.getId(), "userOne");

reviews = (ArrayNode) cmmnRuntimeService.getVariable(caseInstance.getId(), "reviews");

assertThatJson(reviews)
.isEqualTo("["
+ "{ userId: 'userOne' }"
+ "]");

cmmnTaskService.complete(task.getId(), variables);

reviews = (ArrayNode) cmmnRuntimeService.getVariable(caseInstance.getId(), "reviews");

assertThatJson(reviews)
.isEqualTo("["
+ "{ userId: 'userOne', approved : false, description : 'description task 0' },"
+ "{ userId: null }"
+ "]");

assertVariablesNotVisible(caseInstance);

task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskName("My Task").singleResult();
assertThat(cmmnTaskService.getVariable(task.getId(), "repetitionCounter")).isEqualTo(2);
variables.put("approved", true);
variables.put("description", "description task 1");
cmmnTaskService.setAssignee(task.getId(), "userTwo");
cmmnTaskService.complete(task.getId(), variables);
}

@Test
@CmmnDeployment
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/cmmn" xmlns:cmmndi="http://www.omg.org/spec/CMMN/20151109/CMMNDI" xmlns:dc="http://www.omg.org/spec/CMMN/20151109/DC" xmlns:di="http://www.omg.org/spec/CMMN/20151109/DI" targetNamespace="http://www.flowable.org/casedef">
<case id="repeatingTask" name="repeatingTask" flowable:initiatorVariableName="initiator">
<casePlanModel id="casePlanModel">
<planItem id="planItem1" name="My Task" definitionRef="humanTask1">
<itemControl>
<repetitionRule flowable:ignoreCounterVariable="true" />
</itemControl>
</planItem>
<humanTask id="humanTask1" name="My Task"></humanTask>
</casePlanModel>
</case>
<cmmndi:CMMNDI>
<cmmndi:CMMNDiagram id="CMMNDiagram_repeatingTask">
<cmmndi:CMMNShape id="CMMNShape_casePlanModel" cmmnElementRef="casePlanModel">
<dc:Bounds height="714.0" width="718.0" x="40.0" y="40.0"></dc:Bounds>
<cmmndi:CMMNLabel></cmmndi:CMMNLabel>
</cmmndi:CMMNShape>
<cmmndi:CMMNShape id="CMMNShape_planItem1" cmmnElementRef="planItem1">
<dc:Bounds height="80.0" width="100.0" x="183.0" y="218.0"></dc:Bounds>
<cmmndi:CMMNLabel></cmmndi:CMMNLabel>
</cmmndi:CMMNShape>
</cmmndi:CMMNDiagram>
</cmmndi:CMMNDI>
</definitions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/cmmn" xmlns:cmmndi="http://www.omg.org/spec/CMMN/20151109/CMMNDI" xmlns:dc="http://www.omg.org/spec/CMMN/20151109/DC" xmlns:di="http://www.omg.org/spec/CMMN/20151109/DI" targetNamespace="http://www.flowable.org/casedef">
<case id="repeatingTask" name="repeatingTask" flowable:initiatorVariableName="initiator">
<casePlanModel id="casePlanModel">
<planItem id="planItem1" name="My Task" definitionRef="humanTask1">
<itemControl>
<repetitionRule flowable:collectionVariable="myCollection" flowable:elementVariable="item" flowable:elementIndexVariable="itemIndex" flowable:ignoreCounterVariable="true" />
</itemControl>
</planItem>
<humanTask id="humanTask1" name="My Task" flowable:priority="${itemIndex}"></humanTask>
</casePlanModel>
</case>
<cmmndi:CMMNDI>
<cmmndi:CMMNDiagram id="CMMNDiagram_repeatingTask">
<cmmndi:CMMNShape id="CMMNShape_casePlanModel" cmmnElementRef="casePlanModel">
<dc:Bounds height="714.0" width="718.0" x="40.0" y="40.0"></dc:Bounds>
<cmmndi:CMMNLabel></cmmndi:CMMNLabel>
</cmmndi:CMMNShape>
<cmmndi:CMMNShape id="CMMNShape_planItem1" cmmnElementRef="planItem1">
<dc:Bounds height="80.0" width="100.0" x="183.0" y="218.0"></dc:Bounds>
<cmmndi:CMMNLabel></cmmndi:CMMNLabel>
</cmmndi:CMMNShape>
</cmmndi:CMMNDiagram>
</cmmndi:CMMNDI>
</definitions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/cmmn"
xsi:schemaLocation="http://www.omg.org/spec/CMMN/20151109/MODEL https://www.omg.org/spec/CMMN/20151109/CMMN11.xsd"
targetNamespace="http://flowable.org/cmmn">

<case id="repeatingTask" name="repeatingTask" flowable:initiatorVariableName="initiator">
<casePlanModel id="casePlanModel">

<planItem id="planItem1" name="My Task" definitionRef="humanTask1">
<itemControl>
<repetitionRule flowable:ignoreCounterVariable="true">
<extensionElements>
<flowable:variableAggregation target="reviews" createOverviewVariable="true">
<variable sourceExpression="${task:get(taskId).assignee}" target="userId" />
<variable source="approved" />
<variable source="description" />
</flowable:variableAggregation>
</extensionElements>
<condition><![CDATA[${repetitionCounter < nrOfLoops}]]></condition>
</repetitionRule>
</itemControl>
</planItem>

<planItem id="planItem2" name="My Task 2" definitionRef="humanTask2" />

<humanTask id="humanTask1" name="My Task" flowable:taskIdVariableName="taskId"/>

<humanTask id="humanTask2" name="My Task"></humanTask>

</casePlanModel>
</case>

</definitions>
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class RepetitionRule extends PlanItemRule {
public static final String DEFAULT_REPETITION_COUNTER_VARIABLE_NAME = "repetitionCounter";

protected String repetitionCounterVariableName;
protected boolean ignoreRepetitionCounterVariable;
protected String collectionVariableName;
protected String elementVariableName;
protected String elementIndexVariableName;
Expand All @@ -45,6 +46,14 @@ public void setRepetitionCounterVariableName(String repetitionCounterVariableNam
this.repetitionCounterVariableName = repetitionCounterVariableName;
}

public boolean isIgnoreRepetitionCounterVariable() {
return ignoreRepetitionCounterVariable;
}

public void setIgnoreRepetitionCounterVariable(boolean ignoreRepetitionCounterVariable) {
this.ignoreRepetitionCounterVariable = ignoreRepetitionCounterVariable;
}

public String getCollectionVariableName() {
return collectionVariableName;
}
Expand Down

0 comments on commit f4f003c

Please sign in to comment.