diff --git a/CHANGELOG.md b/CHANGELOG.md index 95ab7df4..ec68a633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) - Cleaned up AcknowledgementLetterFromSource example - Fixed handling of styles containing spaces sometimes causing deployment errors - Truncating content preview in area mapping for better readability +- Potential condition variable name collision solved by better naming and positioning under the condition flow/rowset ## [17.0.25] - 2026-06-15 diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt index ce6e903c..a66bd2d5 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt @@ -207,8 +207,11 @@ abstract class InspireDocumentObjectBuilder( protected open fun wrapSuccessFlowInConditionFlow( layout: Layout, variableStructure: VariableStructure, rule: DisplayRule, successFlow: Flow ): Flow { - return layout.addFlow().setType(Flow.Type.SELECT_BY_CONDITION).addLineForSelectByCondition( - layout.data.addVariable().setKind(VariableKind.CALCULATED).setDataType(DataType.BOOL) + val conditionFlow = layout.addFlow().setType(Flow.Type.SELECT_BY_CONDITION) + return conditionFlow.addLineForSelectByCondition( + conditionFlow.addVariable() + .setName(conditionVariableName(rule)) + .setKind(VariableKind.CALCULATED).setDataType(DataType.BOOL) .setScript(rule.toScript( layout, variableStructure, @@ -229,22 +232,27 @@ abstract class InspireDocumentObjectBuilder( innerRowSet: GeneralRowSet? = null, ): GeneralRowSet { val successRow = innerRowSet ?: layout.addRowSet().setType(RowSet.Type.SINGLE_ROW) - return layout.addRowSet().setType(RowSet.Type.SELECT_BY_CONDITION) - .addLineForSelectByCondition( - layout.data.addVariable().setKind(VariableKind.CALCULATED).setDataType(DataType.BOOL) - .setScript(rule.toScript( - layout, - variableStructure, - variableRepository::findOrFail, - displayRuleRepository::findOrFail, - resourcePathProvider::getDisplayRulePath, - output, - projectConfig.interactiveTenant - )), - successRow - ) + val conditionRowSet = layout.addRowSet().setType(RowSet.Type.SELECT_BY_CONDITION) + return conditionRowSet.addLineForSelectByCondition( + conditionRowSet.addVariable() + .setName(conditionVariableName(rule)) + .setKind(VariableKind.CALCULATED).setDataType(DataType.BOOL) + .setScript(rule.toScript( + layout, + variableStructure, + variableRepository::findOrFail, + displayRuleRepository::findOrFail, + resourcePathProvider::getDisplayRulePath, + output, + projectConfig.interactiveTenant + )), + successRow + ) } + private fun conditionVariableName(rule: DisplayRule): String = + "cond_${rule.nameOrId()}" + protected fun wrapRowSetInConditionIfNeeded( layout: Layout, variableStructure: VariableStructure, diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt index 33febfff..4ece656e 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt @@ -411,6 +411,10 @@ class InteractiveDocumentObjectBuilderTest { val successFlowRef = condition[""].stringValue() val conditionVarRef = condition["VarId"].stringValue() + val conditionVarForwardRef = result["Variable"].first { it["Id"].stringValue() == conditionVarRef } + conditionVarForwardRef["ParentId"].stringValue().shouldBeEqualTo(conditionFlowRef) + conditionVarForwardRef["Name"].stringValue().shouldBeEqualTo("cond_R_1") + val conditionVar = result["Variable"].last { it["Id"].stringValue() == conditionVarRef } conditionVar["VarType"].stringValue().shouldBeEqualTo("Bool") conditionVar["Type"].stringValue().shouldBeEqualTo("Calculated") @@ -574,10 +578,15 @@ class InteractiveDocumentObjectBuilderTest { // then val conditionalRow = result["RowSet"].last { it["RowSetType"]?.stringValue() == "Condition" } + val conditionalRowId = conditionalRow["Id"].stringValue() val rowCondition = conditionalRow["RowSetCondition"][0] val subRowRef = rowCondition["SubRowId"].stringValue() val conditionVarRef = rowCondition["VariableId"].stringValue() + val conditionVarForwardRef = result["Variable"].first { it["Id"].stringValue() == conditionVarRef } + conditionVarForwardRef["ParentId"].stringValue().shouldBeEqualTo(conditionalRowId) + conditionVarForwardRef["Name"].stringValue().shouldBeEqualTo("cond_R_1") + val conditionVar = result["Variable"].last { it["Id"].stringValue() == conditionVarRef } conditionVar["Type"].stringValue().shouldBeEqualTo("Calculated") conditionVar["VarType"].stringValue().shouldBeEqualTo("Bool") diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Flow.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Flow.java index 5a13bf62..60783303 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Flow.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Flow.java @@ -8,6 +8,8 @@ public interface Flow extends Node { Paragraph addParagraph(); + Variable addVariable(); + boolean isSectionFlow(); Flow setSectionFlow(boolean sectionFlow); diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/GeneralRowSet.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/GeneralRowSet.java index 93420bb5..4090f3db 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/GeneralRowSet.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/GeneralRowSet.java @@ -4,6 +4,8 @@ public interface GeneralRowSet extends RowSet { + Variable addVariable(); + GeneralRowSet setType(RowSet.Type rowSetType); GeneralRowSet addCell(Cell cell); diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/HasLocalNodes.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/HasLocalNodes.java new file mode 100644 index 00000000..5a9e8ec3 --- /dev/null +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/HasLocalNodes.java @@ -0,0 +1,16 @@ +package com.quadient.wfdxml.internal; + +import java.util.List; + +/** + * Marks a layout node that owns a private list of child nodes (e.g. condition variables) + * which are logically scoped to that node rather than to the global Data root. + *

+ * Implementors supply the list via {@link #getLocalNodes()}; the + * {@code ForwardReferencesExporter} discovers and registers these nodes with a + * {@code ParentId} pointing to the owning node, matching the structure that + * Inspire Designer produces interactively. + */ +public interface HasLocalNodes { + List getLocalNodes(); +} diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/FlowImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/FlowImpl.java index e16fa0b4..75748120 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/FlowImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/FlowImpl.java @@ -2,12 +2,17 @@ import com.quadient.wfdxml.api.layoutnodes.Flow; import com.quadient.wfdxml.api.layoutnodes.data.Variable; +import com.quadient.wfdxml.internal.HasLocalNodes; import com.quadient.wfdxml.internal.NodeImpl; +import com.quadient.wfdxml.internal.layoutnodes.data.LAVariableIface.VariableType; +import com.quadient.wfdxml.internal.layoutnodes.data.VariableImpl; +import com.quadient.wfdxml.internal.layoutnodes.data.WorkFlowTreeEnums.NodeType; import com.quadient.wfdxml.internal.layoutnodes.flow.ParagraphImpl; import com.quadient.wfdxml.internal.layoutnodes.support.FlowContent; import com.quadient.wfdxml.internal.xml.export.XmlExporter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static com.quadient.wfdxml.internal.layoutnodes.FlowImpl.Type.CONDITION; @@ -28,8 +33,9 @@ import static com.quadient.wfdxml.internal.layoutnodes.FlowImpl.Type.TEXT; import static com.quadient.wfdxml.internal.layoutnodes.FlowImpl.Type.VARIABLE_FORMATTED; -public class FlowImpl extends NodeImpl implements Flow { +public class FlowImpl extends NodeImpl implements Flow, HasLocalNodes { private final List flowConditions = new ArrayList<>(); + private final List localNodes = new ArrayList<>(); private Type type = SIMPLE; private Variable variable; private boolean sectionFlow = false; @@ -292,6 +298,18 @@ public FlowImpl setVariable(Variable variable) { return this; } + @Override + public VariableImpl addVariable() { + VariableImpl var = new VariableImpl().setType(VariableType.CONSTANT).setNodeType(NodeType.STRING); + localNodes.add(var); + return var; + } + + @Override + public List getLocalNodes() { + return Collections.unmodifiableList(localNodes); + } + enum Type { SIMPLE, INTEGER, diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImpl.java index dda07b41..a007123b 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImpl.java @@ -6,10 +6,15 @@ import com.quadient.wfdxml.api.layoutnodes.tables.GeneralRowSet; import com.quadient.wfdxml.api.layoutnodes.tables.HeaderFooterRowSet; import com.quadient.wfdxml.api.layoutnodes.tables.RowSet; +import com.quadient.wfdxml.internal.HasLocalNodes; import com.quadient.wfdxml.internal.NodeImpl; +import com.quadient.wfdxml.internal.layoutnodes.data.LAVariableIface.VariableType; +import com.quadient.wfdxml.internal.layoutnodes.data.VariableImpl; +import com.quadient.wfdxml.internal.layoutnodes.data.WorkFlowTreeEnums.NodeType; import com.quadient.wfdxml.internal.xml.export.XmlExporter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static com.quadient.wfdxml.internal.layoutnodes.tables.RowSetImpl.Type.HEADER_FOOTER; @@ -22,8 +27,9 @@ import static com.quadient.wfdxml.internal.layoutnodes.tables.RowSetImpl.Type.SWITCH_INT; import static com.quadient.wfdxml.internal.layoutnodes.tables.RowSetImpl.Type.SWITCH_RANGE; -public class RowSetImpl extends NodeImpl implements GeneralRowSet, HeaderFooterRowSet { +public class RowSetImpl extends NodeImpl implements GeneralRowSet, HeaderFooterRowSet, HasLocalNodes { private final List subRows = new ArrayList<>(); + private final List localNodes = new ArrayList<>(); private final List rowSetConditions = new ArrayList<>(); private Variable variable; private Type rowSetType = ROW; @@ -162,6 +168,18 @@ public Variable getVariable() { return variable; } + @Override + public VariableImpl addVariable() { + VariableImpl var = new VariableImpl().setType(VariableType.CONSTANT).setNodeType(NodeType.STRING); + localNodes.add(var); + return var; + } + + @Override + public List getLocalNodes() { + return Collections.unmodifiableList(localNodes); + } + @Override public RowSetImpl setVariable(Variable variable) { this.variable = variable; diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java index 09657f4d..4f60fb50 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java @@ -2,6 +2,7 @@ import com.quadient.wfdxml.api.layoutnodes.data.Variable; import com.quadient.wfdxml.internal.DefaultNodeType; +import com.quadient.wfdxml.internal.HasLocalNodes; import com.quadient.wfdxml.internal.NodeImpl; import com.quadient.wfdxml.internal.Tree; import com.quadient.wfdxml.internal.xml.export.XmlExporter; @@ -51,10 +52,15 @@ private void exportTree(Tree tree, Boolean useExistingVariables) { if (child instanceof Tree) { exportTree((Tree) child, useExistingVariables); } + if (child instanceof HasLocalNodes holder && !holder.getLocalNodes().isEmpty()) { + for (NodeImpl localNode : holder.getLocalNodes()) { + writeForwardReferenceToExporter(localNode, child, useExistingVariables); + } + } } } - private void writeForwardReferenceToExporter(NodeImpl node, Tree parent, Boolean useExistingVariables) { + private void writeForwardReferenceToExporter(NodeImpl node, NodeImpl parent, Boolean useExistingVariables) { exporter.beginElement(node.getXmlElementName()).addElementWithIface("Id", node).addElementWithStringData("Name", node.getName()).addElementWithStringData("Comment", node.getComment()); if (node instanceof Variable variable && variable.getExistingParentId() != null) { diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java index 3772aee9..3041cf5c 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java @@ -9,6 +9,7 @@ import com.quadient.wfdxml.api.module.Layout; import com.quadient.wfdxml.internal.DefaultNodeType; import com.quadient.wfdxml.internal.Group; +import com.quadient.wfdxml.internal.HasLocalNodes; import com.quadient.wfdxml.internal.NodeImpl; import com.quadient.wfdxml.internal.Tree; import com.quadient.wfdxml.internal.layoutnodes.*; @@ -374,7 +375,14 @@ private void exportTree(Tree tree, XmlExporter exporter) { if (child instanceof Tree) { exportTree((Tree) child, exporter); } - + if (child instanceof HasLocalNodes holder && !holder.getLocalNodes().isEmpty()) { + for (NodeImpl localNode : holder.getLocalNodes()) { + exporter.beginElement(localNode.getXmlElementName()); + exporter.addElementWithIface("Id", localNode); + localNode.export(exporter); + exporter.endElement(); + } + } } } diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/FlowImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/FlowImplTest.groovy index 0b718694..dc659e98 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/FlowImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/FlowImplTest.groovy @@ -312,4 +312,16 @@ class FlowImplTest extends Specification { True """) } + + def "addVariable adds variable to localNodes"() { + given: + FlowImpl flow = new FlowImpl().setType(SELECT_BY_CONDITION) + + when: + def variable = flow.addVariable().setName("cond_MyRule") + + then: + assert flow.localNodes.size() == 1 + assert flow.localNodes[0].is(variable) + } } \ No newline at end of file diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImplTest.groovy index e00f02d3..36194f59 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/RowSetImplTest.groovy @@ -336,4 +336,16 @@ class RowSetImplTest extends Specification { True """) } + + def "addVariable adds variable to localNodes"() { + given: + RowSetImpl rowSet = new RowSetImpl().setType(SELECT_BY_CONDITION) + + when: + def variable = rowSet.addVariable().setName("cond_MyRule") + + then: + assert rowSet.localNodes.size() == 1 + assert rowSet.localNodes[0].is(variable) + } } \ No newline at end of file diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy index ee7afcfe..44595313 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy @@ -8,6 +8,7 @@ import com.quadient.wfdxml.api.layoutnodes.TextStyle import com.quadient.wfdxml.api.layoutnodes.data.DataType import com.quadient.wfdxml.api.layoutnodes.data.VariableKind import com.quadient.wfdxml.api.layoutnodes.tables.Cell +import com.quadient.wfdxml.api.layoutnodes.tables.GeneralRowSet import com.quadient.wfdxml.api.layoutnodes.tables.RowSet import com.quadient.wfdxml.api.layoutnodes.tables.Table import com.quadient.wfdxml.api.module.Layout @@ -208,4 +209,42 @@ class LayoutImplTest extends Specification { then: assert result.contains("Def.FlowGroup{"ValueWrapperVariable":true,"Version":2,"DisplayName":"Flow with multiple properties"}") } + + def "local variable of a condition flow is exported with flow as parentId"() { + given: + Layout layout = new LayoutImpl() + def condFlow = layout.addFlow() + .setType(Flow.Type.SELECT_BY_CONDITION) + .setName("CondFlow") + .setId("CondFlowId") + condFlow.addVariable().setName("cond_MyRule").setKind(VariableKind.CALCULATED).setDataType(DataType.BOOL) + + when: + layout.exportLayoutDelta(exporter) + String result = exporter.buildString() + + then: + assert result.contains("cond_MyRule") + assert result.contains("Calculated") + assert result.contains("CondFlowId") + } + + def "local variable of a condition rowset is exported with rowset as parentId"() { + given: + Layout layout = new LayoutImpl() + def condRowSet = layout.addRowSet() + .setType(RowSet.Type.SELECT_BY_CONDITION) + .setName("CondRowSet") + .setId("CondRowSetId") as GeneralRowSet + condRowSet.addVariable().setName("cond_MyRule").setKind(VariableKind.CALCULATED).setDataType(DataType.BOOL) + + when: + layout.exportLayoutDelta(exporter) + String result = exporter.buildString() + + then: + assert result.contains("cond_MyRule") + assert result.contains("Calculated") + assert result.contains("CondRowSetId") + } } \ No newline at end of file