This is an automated email from the ASF dual-hosted git repository.
gitgabrio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git
The following commit(s) were added to refs/heads/main by this push:
new 5d06eb3546 [incubator-kie-issues#2065] Decision Services doesn't work
If the import name and the inputName is same (#6427)
5d06eb3546 is described below
commit 5d06eb3546e42c5f9743f70c0a3f898e423dd834
Author: Gabriele Cardosi <[email protected]>
AuthorDate: Thu Aug 21 11:07:27 2025 +0200
[incubator-kie-issues#2065] Decision Services doesn't work If the import
name and the inputName is same (#6427)
* [incubator-kie-issues#2065] WIP - more analysis, refactoring and unit
test needed
* [incubator-kie-issues#2065] WIP - Split DMNRuntimeImpl with
DMNRuntimeUtils. Extend test cases with clashing complex type
* [incubator-kie-issues#2065] Refactoring. Extending test coverage
* [incubator-kie-issues#2065] Fix as per PR suggestion
---------
Co-authored-by: Gabriele-Cardosi <[email protected]>
---
.../dmn/core/ast/DMNDecisionServiceEvaluator.java | 9 +-
...DecisionServiceFunctionDefinitionEvaluator.java | 9 +-
.../dmn/core/ast/DMNFunctionWithReturnType.java | 10 +-
.../java/org/kie/dmn/core/impl/DMNRuntimeImpl.java | 173 ++------
.../org/kie/dmn/core/impl/DMNRuntimeUtils.java | 276 ++++++++++++
.../org/kie/dmn/core/impl/DMNRuntimeImplTest.java | 242 -----------
.../org/kie/dmn/core/impl/DMNRuntimeUtilsTest.java | 461 +++++++++++++++++++++
.../java/org/kie/dmn/core/imports/ImportsTest.java | 92 ++++
.../InvalidModelImportInputDataNameClash.dmn | 55 +++
.../invalid_models/DMNv1_6/ParentModel.dmn | 48 +++
.../ImportedModel.dmn | 78 ++++
.../ImportingModel.dmn | 69 +++
.../ImportedModel.dmn | 70 ++++
.../ImportingModel.dmn | 69 +++
.../java/org/kie/dmn/validation/ValidatorTest.java | 44 ++
15 files changed, 1307 insertions(+), 398 deletions(-)
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceEvaluator.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceEvaluator.java
index e8adbc9483..ff752b51c0 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceEvaluator.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceEvaluator.java
@@ -40,6 +40,7 @@ import org.kie.dmn.core.impl.DMNDecisionResultImpl;
import org.kie.dmn.core.impl.DMNResultImpl;
import org.kie.dmn.core.impl.DMNRuntimeEventManagerUtils;
import org.kie.dmn.core.impl.DMNRuntimeImpl;
+import org.kie.dmn.core.impl.DMNRuntimeUtils;
import org.kie.dmn.core.util.Msg;
import org.kie.dmn.core.util.MsgUtil;
import org.slf4j.Logger;
@@ -84,10 +85,10 @@ public class DMNDecisionServiceEvaluator implements
DMNExpressionEvaluator {
}
boolean typeCheck = ((DMNRuntimeImpl)
eventManager.getRuntime()).performRuntimeTypeCheck(result.getModel());
if (typeCheck) {
- Object c = DMNRuntimeImpl.coerceUsingType(decisionIDs.size() == 1
? ctx.values().iterator().next() : ctx,
- dsNode.getResultType(),
- typeCheck,
- (rx, tx) ->
MsgUtil.reportMessage(LOG,
+ Object c = DMNRuntimeUtils.coerceUsingType(decisionIDs.size() == 1
? ctx.values().iterator().next() : ctx,
+ dsNode.getResultType(),
+ typeCheck,
+ (rx, tx) ->
MsgUtil.reportMessage(LOG,
DMNMessage.Severity.WARN,
dsNode.getDecisionService(),
result,
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceFunctionDefinitionEvaluator.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceFunctionDefinitionEvaluator.java
index e612092067..d9053581c9 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceFunctionDefinitionEvaluator.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDecisionServiceFunctionDefinitionEvaluator.java
@@ -36,6 +36,7 @@ import org.kie.dmn.api.core.EvaluatorResult.ResultType;
import org.kie.dmn.core.ast.DMNFunctionDefinitionEvaluator.FormalParameter;
import org.kie.dmn.core.impl.DMNResultImpl;
import org.kie.dmn.core.impl.DMNRuntimeImpl;
+import org.kie.dmn.core.impl.DMNRuntimeUtils;
import org.kie.dmn.core.util.Msg;
import org.kie.dmn.core.util.MsgUtil;
import org.kie.dmn.feel.lang.EvaluationContext;
@@ -156,10 +157,10 @@ public class
DMNDecisionServiceFunctionDefinitionEvaluator implements DMNExpress
private Object performTypeCheckIfNeeded(Object param, int paramIndex) {
DSFormalParameter dsFormalParameter = parameters.get(paramIndex);
- Object result = DMNRuntimeImpl.coerceUsingType(param,
-
dsFormalParameter.type,
- typeCheck,
- (rx, tx) ->
MsgUtil.reportMessage(LOG,
+ Object result = DMNRuntimeUtils.coerceUsingType(param,
+
dsFormalParameter.type,
+ typeCheck,
+ (rx, tx) ->
MsgUtil.reportMessage(LOG,
DMNMessage.Severity.ERROR,
null,
resultContext,
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionWithReturnType.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionWithReturnType.java
index d226ab2df3..ebad93794e 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionWithReturnType.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionWithReturnType.java
@@ -24,7 +24,7 @@ import org.kie.dmn.api.core.DMNMessage;
import org.kie.dmn.api.core.DMNType;
import org.kie.dmn.api.core.ast.BusinessKnowledgeModelNode;
import org.kie.dmn.core.api.DMNMessageManager;
-import org.kie.dmn.core.impl.DMNRuntimeImpl;
+import org.kie.dmn.core.impl.DMNRuntimeUtils;
import org.kie.dmn.core.util.Msg;
import org.kie.dmn.core.util.MsgUtil;
import org.kie.dmn.feel.lang.EvaluationContext;
@@ -59,10 +59,10 @@ public class DMNFunctionWithReturnType extends
BaseFEELFunction {
@Override
public Object invokeReflectively(EvaluationContext ctx, Object[] params) {
Object result = wrapped.invokeReflectively(ctx, params);
- result = DMNRuntimeImpl.coerceUsingType(result,
- returnType,
- true, // this FN is created
when typeCheck==true, hence here always true.
- (r, t) ->
MsgUtil.reportMessage(LOG,
+ result = DMNRuntimeUtils.coerceUsingType(result,
+ returnType,
+ true, // this FN is created
when typeCheck==true, hence here always true.
+ (r, t) ->
MsgUtil.reportMessage(LOG,
DMNMessage.Severity.WARN,
node.getBusinessKnowledModel(),
msgMgr,
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
index cef231ea66..967ca8b030 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
@@ -68,6 +68,11 @@ import static
org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.EV
import static
org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.FAILED;
import static
org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.SKIPPED;
import static org.kie.dmn.core.compiler.UnnamedImportUtils.isInUnnamedImport;
+import static org.kie.dmn.core.impl.DMNRuntimeUtils.coerceUsingType;
+import static org.kie.dmn.core.impl.DMNRuntimeUtils.getDependencyIdentifier;
+import static org.kie.dmn.core.impl.DMNRuntimeUtils.getIdentifier;
+import static org.kie.dmn.core.impl.DMNRuntimeUtils.getObjectString;
+import static
org.kie.dmn.core.impl.DMNRuntimeUtils.populateResultContextWithTopmostParentsValues;
import static org.kie.dmn.core.util.CoerceUtil.coerceValue;
public class DMNRuntimeImpl
@@ -91,84 +96,6 @@ public class DMNRuntimeImpl
}
}
- static void populateResultContextWithTopmostParentsValues(DMNContext
context, DMNModelImpl model) {
- Optional<Set<DMNModelImpl.ModelImportTuple>> optionalTopmostModels =
getTopmostModel(model);
- optionalTopmostModels.ifPresent(topmostModels ->
populateInputsFromTopmostModel(context, model, topmostModels));
- }
-
- static void populateInputsFromTopmostModel(DMNContext context,DMNModelImpl
model, Set<DMNModelImpl.ModelImportTuple> topmostModels) {
- for (DMNModelImpl.ModelImportTuple topmostModelTuple : topmostModels) {
- processTopmostModelTuple(context, topmostModelTuple, model);
- }
- }
-
- static void processTopmostModelTuple(DMNContext context,
DMNModelImpl.ModelImportTuple topmostModelTuple, DMNModelImpl model) {
- DMNModelImpl topmostModel = topmostModelTuple.getModel();
- for (InputDataNode topmostInput : topmostModel.getInputs()) {
- processTopmostModelInputDataNode(context, topmostInput.getName(),
topmostModelTuple, model);
- }
- }
-
- static void processTopmostModelInputDataNode( DMNContext context, String
topmostInputName, DMNModelImpl.ModelImportTuple topmostModelTuple, DMNModelImpl
model) {
- Object storedValue = context.get(topmostInputName);
- if (storedValue != null) {
- Object parentData = context.get(topmostModelTuple.getImportName());
- if (parentData instanceof Map mappedData) {
- processTopmostModelMap(mappedData, topmostInputName,
storedValue, parentData);
- } else if (parentData == null) {
- updateContextMap(context, model.getImportChainAliases(),
topmostModelTuple, topmostInputName, storedValue);
- }
- }
- }
-
- /**
- * Depending on how the context has been instantiated, the provided
<code>Map</code> could be unmodifiable, which is an expected condition
- * @param mappedData
- * @param inputName
- * @param storedValue
- * @param parentData
- */
- static void processTopmostModelMap(Map mappedData, String inputName,
Object storedValue, Object parentData) {
- try {
- mappedData.put(inputName, storedValue);
- } catch (Exception e) {
- logger.warn("Failed to add {} to map {} ", storedValue,
parentData, e);
- }
- }
-
- static void updateContextMap(DMNContext context, Map<String,
Collection<List<String>>> importChainAliases, DMNModelImpl.ModelImportTuple
topmostModelTuple, String inputName, Object storedValue) {
- Map mappedData = new HashMap<>();
- mappedData.put(inputName, storedValue);
- populateContextWithInheritedData(context, mappedData,
- topmostModelTuple.getImportName(),
topmostModelTuple.getModel().getNamespace(), importChainAliases);
- }
-
- static void populateContextWithInheritedData(DMNContext toPopulate,
Map<String, Object> toStore, String importName, String
topmostNamespace,Map<String, Collection<List<String>>> importChainAliases) {
- for (List<String> chainedModels :
importChainAliases.get(topmostNamespace)) {
- // The order is: first one -> importing model; last one -> parent
model
- for (String chainedModel : chainedModels) {
- if (chainedModel.equals(importName)) {
- continue;
- }
- if (toStore.get(chainedModel) != null &&
toStore.get(chainedModel) instanceof Map<?, ?> alreadyMapped) {
- try {
- ((Map<String, Object>) alreadyMapped).put(importName,
toStore);
- } catch (Exception e) {
- logger.warn("Failed to add {} to map {} ", toStore,
alreadyMapped, e);
- }
- } else {
- Map<String, Object> chainedMap = new HashMap<>();
- chainedMap.put(importName, toStore);
- toPopulate.set(chainedModel, chainedMap);
- }
- }
- }
- }
-
- static Optional<Set<DMNModelImpl.ModelImportTuple>>
getTopmostModel(DMNModelImpl model) {
- return model.getTopmostParents();
- }
-
@Override
public List<DMNModel> getModels() {
return runtimeKB.getModels();
@@ -196,7 +123,8 @@ public class DMNRuntimeImpl
boolean strictMode =
this.runtimeModeOption.equals(RuntimeModeOption.MODE.STRICT);
DMNResultImpl result = createResult(model, context);
DMNRuntimeEventManagerUtils.fireBeforeEvaluateAll(eventManager, model,
result);
- // the engine should evaluate all Decisions belonging to the "local"
model namespace, not imported decision explicitly.
+ // the engine should evaluate all Decisions belonging to the "local"
model namespace, not imported decision
+ // explicitly.
Set<DecisionNode> decisions = model.getDecisions().stream()
.filter(d ->
d.getModelNamespace().equals(model.getNamespace())).collect(Collectors.toSet());
for (DecisionNode decision : decisions) {
@@ -347,7 +275,8 @@ public class DMNRuntimeImpl
public DMNResult evaluateDecisionService(DMNModel model, DMNContext
context, String decisionServiceName) {
Objects.requireNonNull(model, () ->
MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL, "model"));
Objects.requireNonNull(context, () ->
MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL, "context"));
- Objects.requireNonNull(decisionServiceName, () ->
MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL, "decisionServiceName"));
+ Objects.requireNonNull(decisionServiceName, () ->
MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL,
+
"decisionServiceName"));
boolean typeCheck = performRuntimeTypeCheck(model);
DMNResultImpl result = createResultImpl(model, context);
@@ -539,49 +468,16 @@ public class DMNRuntimeImpl
}
}
- public static Object coerceUsingType(Object value, DMNType type, boolean
typeCheck,
- BiConsumer<Object, DMNType>
nullCallback) {
- if (typeCheck) {
- if (type.isAssignableValue(value)) {
- return coerceSingleItemCollectionToValue(value, type);
- } else {
- nullCallback.accept(value, type);
- return null;
- }
- } else {
- return coerceSingleItemCollectionToValue(value, type);
- }
- }
-
- /**
- * Checks a type and if it is not a collection type, checks if the
specified value is a collection
- * that contains only a single value and if yes, coerces the collection to
the single item itself.
- * E.g. [1] becomes 1. Basically it unwraps the single item from a
collection, if it is required.
- * @param value Value that is checked and potentially coerced to a single
item.
- * @param type Required type. Based on this type, it is determined, if the
coercion happens.
- * If the requirement is for a non-collection type and the value is a
single item collection,
- * the coercion happens.
- * @return If all requirements are met, returns coerced value. Otherwise
returns the original value.
- */
- private static Object coerceSingleItemCollectionToValue(Object value,
DMNType type) {
- if (!type.isCollection() && value instanceof Collection &&
((Collection<?>) value).size() == 1) {
- // as per Decision evaluation result.
- return ((Collection<?>) value).toArray()[0];
- } else {
- return value;
- }
- }
-
private boolean isNodeValueDefined(DMNResultImpl result, DMNNode
callerNode, DMNNode calledNode) {
if
(calledNode.getModelNamespace().equals(result.getContext().scopeNamespace().orElse(result.getModel()
-
.getNamespace()))) {
+
.getNamespace()))) {
return result.getContext().isDefined(calledNode.getName());
- } else if (isInUnnamedImport(calledNode, (DMNModelImpl)
result.getModel())) {
+ } else if (isInUnnamedImport(calledNode, (DMNModelImpl)
result.getModel())) {
// the node is an unnamed import
return result.getContext().isDefined(calledNode.getName());
} else {
Optional<String> importAlias =
callerNode.getModelImportAliasFor(calledNode.getModelNamespace(), calledNode
- .getModelName());
+ .getModelName());
if (importAlias.isPresent()) {
Object aliasContext =
result.getContext().get(importAlias.get());
if (aliasContext instanceof Map<?, ?> mappedContext) {
@@ -598,7 +494,8 @@ public class DMNRuntimeImpl
return false;
} else {
DMNModelImpl model = (DMNModelImpl) dmnModel;
- Optional<String> importAlias =
model.getImportAliasFor(destinationNode.getModelNamespace(),
destinationNode.getModelName());
+ Optional<String> importAlias =
model.getImportAliasFor(destinationNode.getModelNamespace(),
+
destinationNode.getModelName());
if (importAlias.isPresent()) {
result.getContext().pushScope(importAlias.get(),
destinationNode.getModelNamespace());
return true;
@@ -625,7 +522,8 @@ public class DMNRuntimeImpl
// the destinationNode is an unnamed import
return false;
} else {
- Optional<String> importAlias =
callerNode.getModelImportAliasFor(destinationNode.getModelNamespace(),
destinationNode.getModelName());
+ Optional<String> importAlias =
callerNode.getModelImportAliasFor(destinationNode.getModelNamespace(),
+
destinationNode.getModelName());
if (importAlias.isPresent()) {
result.getContext().pushScope(importAlias.get(),
destinationNode.getModelNamespace());
return true;
@@ -637,7 +535,8 @@ public class DMNRuntimeImpl
null,
null,
Msg.IMPORT_NOT_FOUND_FOR_NODE_MISSING_ALIAS,
- new
QName(destinationNode.getModelNamespace(), destinationNode.getModelName()),
+ new
QName(destinationNode.getModelNamespace(),
+
destinationNode.getModelName()),
callerNode.getName()
);
return false;
@@ -660,7 +559,8 @@ public class DMNRuntimeImpl
null,
null,
Msg.IMPORT_NOT_FOUND_FOR_NODE_MISSING_ALIAS,
- new
QName(destinationNode.getModelNamespace(), destinationNode.getModelName()),
+ new
QName(destinationNode.getModelNamespace(),
+
destinationNode.getModelName()),
callerNode.getName());
return false;
}
@@ -678,16 +578,19 @@ public class DMNRuntimeImpl
return true;
} else {
// check if the decision was already evaluated before and returned
error
- DMNDecisionResult.DecisionEvaluationStatus status =
Optional.ofNullable(result.getDecisionResultById(decisionId))
+ DMNDecisionResult.DecisionEvaluationStatus status =
+
Optional.ofNullable(result.getDecisionResultById(decisionId))
.map(DMNDecisionResult::getEvaluationStatus)
-
.orElse(DMNDecisionResult.DecisionEvaluationStatus.NOT_EVALUATED); // it might
be an imported Decision.
+
.orElse(DMNDecisionResult.DecisionEvaluationStatus.NOT_EVALUATED); // it might
be an imported
+ // Decision.
if (FAILED == status || SKIPPED == status || EVALUATING == status)
{
return false;
}
}
BeforeEvaluateDecisionEvent beforeEvaluateDecisionEvent = null;
try {
- beforeEvaluateDecisionEvent =
DMNRuntimeEventManagerUtils.fireBeforeEvaluateDecision(eventManager, decision,
result);
+ beforeEvaluateDecisionEvent =
DMNRuntimeEventManagerUtils.fireBeforeEvaluateDecision(eventManager,
+
decision, result);
DMNDecisionResultImpl dr = (DMNDecisionResultImpl)
result.getDecisionResultById(decisionId);
if (dr == null) { // an imported Decision now evaluated, requires
the creation of the decision result:
String decisionResultName = d.getName();
@@ -697,7 +600,8 @@ public class DMNRuntimeImpl
decisionResultName = importAliasFor.get() + "." +
d.getName();
}
dr = new DMNDecisionResultImpl(decisionId, decisionResultName);
- if (importAliasFor.isPresent()) { // otherwise is a
transitive, skipped and not to be added to the results:
+ if (importAliasFor.isPresent()) { // otherwise is a
transitive, skipped and not to be added to the
+ // results:
result.addDecisionResult(dr);
}
}
@@ -859,6 +763,7 @@ public class DMNRuntimeImpl
try {
if (typeCheck && !checkDependencyValueIsValid(dep, result)) {
toReturn = true;
+ String toPrint =
getObjectString(result.getContext().get(dep.getName()));
DMNMessage message = MsgUtil.reportMessage(logger,
DMNMessage.Severity.ERROR,
((DMNBaseNode)
dep).getSource(),
@@ -868,7 +773,7 @@ public class DMNRuntimeImpl
Msg.ERROR_EVAL_NODE_DEP_WRONG_TYPE,
getIdentifier(decision),
getDependencyIdentifier(decision, dep),
-
MsgUtil.clipString(Objects.toString(result.getContext().get(dep.getName())),
50),
+ toPrint,
((DMNBaseNode)
dep).getType()
);
reportFailure(dr, message,
DMNDecisionResult.DecisionEvaluationStatus.SKIPPED);
@@ -887,24 +792,6 @@ public class DMNRuntimeImpl
return toReturn;
}
- private static String getIdentifier(DMNNode node) {
- return node.getName() != null ? node.getName() : node.getId();
- }
-
- private static String getDependencyIdentifier(DMNNode callerNode, DMNNode
node) {
- if (node.getModelNamespace().equals(callerNode.getModelNamespace())) {
- return getIdentifier(node);
- } else {
- Optional<String> importAlias =
callerNode.getModelImportAliasFor(node.getModelNamespace(),
-
node.getModelName());
- String prefix = "{" + node.getModelNamespace() + "}";
- if (importAlias.isPresent()) {
- prefix = importAlias.get();
- }
- return prefix + "." + getIdentifier(node);
- }
- }
-
public boolean performRuntimeTypeCheck(DMNModel model) {
Objects.requireNonNull(model, () ->
MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL, "model"));
return overrideRuntimeTypeCheck || ((DMNModelImpl)
model).isRuntimeTypeCheck();
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeUtils.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeUtils.java
new file mode 100644
index 0000000000..1158e1539a
--- /dev/null
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeUtils.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kie.dmn.core.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import org.kie.dmn.api.core.DMNContext;
+import org.kie.dmn.api.core.DMNType;
+import org.kie.dmn.api.core.ast.DMNNode;
+import org.kie.dmn.api.core.ast.InputDataNode;
+import org.kie.dmn.core.util.MsgUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class to support <code>DMNRuntimeImpl</code>
+ */
+public class DMNRuntimeUtils {
+
+ private static final Logger logger =
LoggerFactory.getLogger(DMNRuntimeUtils.class);
+
+ private DMNRuntimeUtils() {
+ // singleton
+ }
+
+
+ public static Object coerceUsingType(Object value, DMNType type, boolean
typeCheck,
+ BiConsumer<Object, DMNType>
nullCallback) {
+ if (typeCheck) {
+ if (type.isAssignableValue(value)) {
+ return coerceSingleItemCollectionToValue(value, type);
+ } else {
+ nullCallback.accept(value, type);
+ return null;
+ }
+ } else {
+ return coerceSingleItemCollectionToValue(value, type);
+ }
+ }
+
+ static void populateResultContextWithTopmostParentsValues(DMNContext
context, DMNModelImpl model) {
+ Optional<Set<DMNModelImpl.ModelImportTuple>> optionalTopmostModels =
getTopmostModel(model);
+ optionalTopmostModels.ifPresent(topmostModels ->
populateInputsFromTopmostModel(context, model, topmostModels));
+ }
+
+ static void populateInputsFromTopmostModel(DMNContext context,
DMNModelImpl model,
+
Set<DMNModelImpl.ModelImportTuple> topmostModels) {
+ for (DMNModelImpl.ModelImportTuple topmostModelTuple : topmostModels) {
+ processTopmostModelTuple(context, topmostModelTuple, model);
+ }
+ }
+
+ static void processTopmostModelTuple(DMNContext context,
DMNModelImpl.ModelImportTuple topmostModelTuple,
+ DMNModelImpl model) {
+ DMNModelImpl topmostModel = topmostModelTuple.getModel();
+ for (InputDataNode topmostInput : topmostModel.getInputs()) {
+ processTopmostModelInputDataNode(context, topmostInput.getName(),
topmostModelTuple, model);
+ }
+ }
+
+ static void processTopmostModelInputDataNode(DMNContext context, String
topmostInputName,
+ DMNModelImpl.ModelImportTuple
topmostModelTuple, DMNModelImpl model) {
+ if (Objects.equals(topmostInputName,
topmostModelTuple.getImportName())) {
+ processTopmostModelInputDataNodeWithClashingNames(context,
topmostInputName,
+
topmostModelTuple, model);
+ } else {
+ processTopmostModelInputDataNodeWithoutClashingNames(context,
topmostInputName,
+
topmostModelTuple, model);
+ }
+ }
+
+ static void processTopmostModelInputDataNodeWithClashingNames(DMNContext
context, String topmostInputName,
+
DMNModelImpl.ModelImportTuple topmostModelTuple,
+ DMNModelImpl
model) {
+ Object storedValue = context.get(topmostInputName); // This could be
either a raw value, or a map
+ if (storedValue instanceof Map storedMap &&
storedMap.containsKey(topmostInputName)) { // The check is needed to avoid
looping reference
+ return;
+ }
+ // Eventually, we need to create a map with the importing name and
populate it with the input data
+ replaceContextMap(context, topmostModelTuple, topmostInputName,
+ storedValue);
+ }
+
+ static void
processTopmostModelInputDataNodeWithoutClashingNames(DMNContext context, String
topmostInputName,
+
DMNModelImpl.ModelImportTuple topmostModelTuple,
+
DMNModelImpl model) {
+ Object storedValue = context.get(topmostInputName);
+ if (storedValue != null) {
+ Object parentData = context.get(topmostModelTuple.getImportName());
+ if (parentData instanceof Map mappedData) {
+ addValueInsideMap(mappedData, topmostInputName, storedValue);
+ } else if (parentData == null) {
+ updateContextMap(context, model.getImportChainAliases(),
topmostModelTuple, topmostInputName,
+ storedValue);
+ }
+ }
+ }
+
+ static void replaceContextMap(DMNContext context,
+ DMNModelImpl.ModelImportTuple
topmostModelTuple, String inputName,
+ Object storedValue) {
+ Map<String, Object> mappedData = new HashMap<>();
+ mappedData.put(inputName, storedValue);
+ context.set(topmostModelTuple.getImportName(), mappedData);
+ }
+
+ static void updateContextMap(DMNContext context, Map<String,
Collection<List<String>>> importChainAliases,
+ DMNModelImpl.ModelImportTuple
topmostModelTuple, String inputName,
+ Object storedValue) {
+ Map<String, Object> mappedData = new HashMap<>();
+ mappedData.put(inputName, storedValue);
+ populateContextWithInheritedData(context, mappedData,
+ topmostModelTuple.getImportName(),
+
topmostModelTuple.getModel().getNamespace(), importChainAliases);
+ }
+
+ /**
+ * This method populate the given <code>DMNContext</code> with a new entry
whose key is the import name and whose value is the provided <code>Map</code>
<b>toStore</b>,
+ * if those are related with the namespace of the topmost parent
+ * @param toPopulate
+ * @param toStore
+ * @param importName
+ * @param topmostNamespace
+ * @param importChainAliases
+ */
+ static void populateContextWithInheritedData(DMNContext toPopulate,
Map<String, Object> toStore,
+ String importName, String
topmostNamespace, Map<String,
+ Collection<List<String>>> importChainAliases) {
+ for (List<String> chainedModels :
importChainAliases.get(topmostNamespace)) {
+ // The order is: first one -> importing model; last one -> parent
model
+ for (String chainedModel : chainedModels) {
+ if (chainedModel.equals(importName)) {
+ continue;
+ }
+ if (toStore.get(chainedModel) != null &&
toStore.get(chainedModel) instanceof Map alreadyMapped) {
+ addValueInsideMap(alreadyMapped, importName, toStore);
+ } else {
+ addNewMapToContext(toPopulate, importName, toStore,
chainedModel);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a new <code>Map</code> with the given <code>importName</code>
value set to the given <code>Map</code> <b>toStore</b>.
+ * Then, set this newly-created Map in the given <code>DMNContext</code>
as <b>chainedModel</b>
+ * @param toPopulate
+ * @param importName
+ * @param toStore
+ * @param chainedModel
+ */
+ static void addNewMapToContext(DMNContext toPopulate, String importName,
Map<String, Object> toStore, String chainedModel) {
+ Map<String, Object> chainedMap = new HashMap<>();
+ chainedMap.put(importName, toStore);
+ toPopulate.set(chainedModel, chainedMap);
+ }
+
+ /**
+ * Depending on how the context has been instantiated, the provided
<code>Map</code> could be unmodifiable, which
+ * is an expected condition
+ * @param mappedData
+ * @param inputName
+ * @param storedValue
+ */
+ static void addValueInsideMap(Map<String, Object> mappedData, String
inputName, Object storedValue) {
+ try {
+ mappedData.put(inputName, storedValue);
+ } catch (Exception e) {
+ logger.warn("Failed to put {} -> {} to map {} ", inputName,
storedValue, mappedData, e);
+ }
+ }
+
+ /**
+ * Method to return the <b>topmost parents</b> of the given model.
+ * There could be more then one, since a model may import multiple others.
+ * It is a <code>Set</code> to avoid duplicating the same entry
+ * @param model
+ * @return
+ */
+ static Optional<Set<DMNModelImpl.ModelImportTuple>>
getTopmostModel(DMNModelImpl model) {
+ return model.getTopmostParents();
+ }
+
+ /**
+ * Checks a type and if it is not a collection type, checks if the
specified value is a collection
+ * that contains only a single value and if yes, coerces the collection to
the single item itself.
+ * E.g. [1] becomes 1. Basically it unwraps the single item from a
collection, if it is required.
+ * @param value Value that is checked and potentially coerced to a single
item.
+ * @param type Required type. Based on this type, it is determined, if the
coercion happens.
+ * If the requirement is for a non-collection type and the value is a
single item collection,
+ * the coercion happens.
+ * @return If all requirements are met, returns coerced value. Otherwise
returns the original value.
+ */
+ static Object coerceSingleItemCollectionToValue(Object value, DMNType
type) {
+ if (!type.isCollection() && value instanceof Collection &&
((Collection<?>) value).size() == 1) {
+ // as per Decision evaluation result.
+ return ((Collection<?>) value).toArray()[0];
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * Method used to catch StackOverflowError and allow nice handling of them
+ * @return
+ */
+ static String getObjectString(Object toPrint) {
+ try {
+ return MsgUtil.clipString(Objects.toString(toPrint), 50);
+ } catch (StackOverflowError e) {
+ logger.error("Stack overflow error while trying to String {}",
toPrint.getClass());
+ return "(_undefined_)";
+ }
+ }
+
+ /**
+ * If the given nodes have the same namespace, returns the
<code>getIdentifier</code> of the <code>calledNode</code>
+ * Otherwise, returns the <code>getPrefixedIdentifier</code> of the
<code>calledNode</code>
+ * @param callerNode
+ * @param calledNode
+ * @return
+ */
+ static String getDependencyIdentifier(DMNNode callerNode, DMNNode
calledNode) {
+ if
(calledNode.getModelNamespace().equals(callerNode.getModelNamespace())) {
+ return getIdentifier(calledNode);
+ } else {
+ return getPrefixedIdentifier(callerNode, calledNode);
+ }
+ }
+
+ /**
+ * Returns the <code>getIdentifier</code> of the <code>calledNode</code>
prefixed.
+ * If the <code>callerNode</code> has an import alias for the
<code>calledNode</code>, the prefix is the import
+ * alias, otherwise the prefix is the namespace of the
<code>calledNode</code>
+ * @param callerNode
+ * @param calledNode
+ * @return
+ */
+ static String getPrefixedIdentifier(DMNNode callerNode, DMNNode
calledNode) {
+ Optional<String> importAlias =
callerNode.getModelImportAliasFor(calledNode.getModelNamespace(),
+
calledNode.getModelName());
+ String prefix = importAlias.orElse(String.format("{%s}",
calledNode.getModelNamespace()));
+ return prefix + "." + getIdentifier(calledNode);
+ }
+
+ /**
+ * Returns the name of the node, if set, otherwise the id
+ * @param node
+ * @return
+ */
+ static String getIdentifier(DMNNode node) {
+ return node.getName() != null ? node.getName() : node.getId();
+ }
+}
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/impl/DMNRuntimeImplTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/impl/DMNRuntimeImplTest.java
deleted file mode 100644
index 786e8c71c4..0000000000
---
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/impl/DMNRuntimeImplTest.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.kie.dmn.core.impl;
-
-import org.junit.jupiter.api.Test;
-import org.kie.dmn.api.core.DMNContext;
-import org.kie.dmn.api.core.ast.InputDataNode;
-import org.kie.dmn.core.ast.InputDataNodeImpl;
-import org.kie.dmn.model.api.Definitions;
-import org.kie.dmn.model.api.InputData;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.kie.dmn.core.impl.DMNRuntimeImpl.getTopmostModel;
-import static
org.kie.dmn.core.impl.DMNRuntimeImpl.populateContextWithInheritedData;
-import static
org.kie.dmn.core.impl.DMNRuntimeImpl.populateInputsFromTopmostModel;
-import static
org.kie.dmn.core.impl.DMNRuntimeImpl.populateResultContextWithTopmostParentsValues;
-import static org.kie.dmn.core.impl.DMNRuntimeImpl.processTopmostModelTuple;
-import static org.kie.dmn.core.impl.DMNRuntimeImpl.updateContextMap;
-import static org.mockito.Mockito.CALLS_REAL_METHODS;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-class DMNRuntimeImplTest {
-
- @Test
- void testGetTopmostModel() {
- DMNModelImpl importingModel = mock(DMNModelImpl.class);
- DMNModelImpl importedModel = mock(DMNModelImpl.class);
-
- when(importedModel.getNamespace())
-
.thenReturn("http://www.trisotech.com/definitions/_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9");
- when(importedModel.getName())
- .thenReturn("ParentModel");
-
- when(importingModel.getImportAliasFor(importedModel.getNamespace(),
importedModel.getName()))
- .thenReturn(Optional.of("parentModel"));
-
- DMNModelImpl.ModelImportTuple tuple =
- new DMNModelImpl.ModelImportTuple("parentModel",
importedModel);
- Optional<Set<DMNModelImpl.ModelImportTuple>> optionalTopmostModels =
Optional.of(Set.of(tuple));
-
-
when(importingModel.getTopmostParents()).thenReturn(optionalTopmostModels);
-
- Optional<Set<DMNModelImpl.ModelImportTuple>> topmostModel =
getTopmostModel(importingModel);
-
- assertThat(topmostModel).isPresent();
- topmostModel.ifPresent(set ->
- assertThat(set)
-
.extracting(DMNModelImpl.ModelImportTuple::getImportName)
- .containsOnly("parentModel")
- );
- }
-
- @Test
- void testPopulateResultContextWithTopmostParentsValues() {
- Definitions defs = mock(Definitions.class);
- when(defs.getNamespace()).thenReturn("ns1");
- when(defs.getName()).thenReturn("model1");
- DMNModelImpl model = spy(new DMNModelImpl(defs));
-
- Definitions parentDefs = mock(Definitions.class);
- when(parentDefs.getNamespace()).thenReturn("ns2");
- when(parentDefs.getName()).thenReturn("parentModel");
- DMNModelImpl parent = new DMNModelImpl(parentDefs);
-
- InputDataNode inputNode = mock(InputDataNode.class);
- when(inputNode.getName()).thenReturn("inputA");
- when(inputNode.getModelNamespace()).thenReturn("ns2");
- when(inputNode.getId()).thenReturn("idA");
- parent.addInput(inputNode);
-
- model.setImportAliasForNS("parentAlias", "ns2", "parentModel");
- Set<DMNModelImpl.ModelImportTuple> topmostParents =
- Set.of(new DMNModelImpl.ModelImportTuple("parentAlias",
parent));
- doReturn(Optional.of(topmostParents)).when(model).getTopmostParents();
-
- DMNContext context = new DMNContextImpl();
- Map<String, Object> parentAliasMap = new HashMap<>();
- parentAliasMap.put("inputA", "valueA");
- context.set("parentAlias", parentAliasMap);
-
- DMNModelImpl.ModelImportTuple tuple = new
DMNModelImpl.ModelImportTuple("parentModel", parent);
- Optional<Set<DMNModelImpl.ModelImportTuple>> optionalTopmostModels =
Optional.of(Set.of(tuple));
- when(getTopmostModel(model)).thenReturn(optionalTopmostModels);
-
- Map<String, Collection<List<String>>> mockImportChainAliases = new
HashMap<>();
- mockImportChainAliases.put("ns2", List.of(List.of("parentAlias")));
- when(model.getImportChainAliases()).thenReturn(mockImportChainAliases);
-
- populateResultContextWithTopmostParentsValues(context, model);
-
- DMNContext expectedContext = new DMNContextImpl();
- expectedContext.set("parentAlias",Map.of("inputA", "valueA"));
-
- assertThat(context)
- .usingRecursiveComparison()
- .isEqualTo(expectedContext);
- }
-
- @Test
- void testPopulateInputsFromTopmostModels() {
- DMNModelImpl importingModel = mock(DMNModelImpl.class);
- DMNContext context = new DMNContextImpl();
- context.set("Person Name", "Klaus");
-
- InputData mockInputData = mock(InputData.class);
- when(mockInputData.getName()).thenReturn("Person Name");
- InputDataNodeImpl node = new InputDataNodeImpl(mockInputData);
-
- DMNModelImpl topmostModel = mock(DMNModelImpl.class);
- when(topmostModel.getInputs()).thenReturn(Set.of(node));
-
- String namespace = "test-namespace";
- DMNModelImpl.ModelImportTuple tupleA =
mock(DMNModelImpl.ModelImportTuple.class);
- when(tupleA.getModel()).thenReturn(topmostModel);
- when(tupleA.getImportName()).thenReturn("parentModel");
- when(tupleA.getModel().getNamespace()).thenReturn(namespace);
-
- Map<String, Collection<List<String>>> importChainAliases = new
HashMap<>();
- List<String> chain = Arrays.asList("Child A", "parentModel");
- importChainAliases.put(namespace, Collections.singletonList(chain));
-
when(importingModel.getImportChainAliases()).thenReturn(importChainAliases);
-
- Set<DMNModelImpl.ModelImportTuple> topmostModels = Set.of(tupleA);
-
- populateInputsFromTopmostModel(context, importingModel, topmostModels);
-
- assertThat(context.get("Person Name")).isEqualTo("Klaus");
- }
-
- @Test
- void testPopulateContextWithInheritedData() {
- DMNContext toPopulate = new DMNContextImpl();
- toPopulate.set("Person Name", "Klaus");
-
- List<String> chainedModels = List.of("Child A", "parentModel");
- Map toStore = Map.of("Person Name", "Klaus");
- String importName = "parentModel";
- String topMostNamespace =
"https://www.apache.org/customnamespace/_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9";
-
- DMNContext context = new DMNContextImpl();
- Map<String, Object> parentModel = Map.of("Person Name", "Klaus");
- Map<String, Object> childA = Map.of("parentModel", parentModel);
- context.set("Person Name", "Klaus");
- context.set("Child A", childA);
-
- Map<String, Collection<List<String>>> importChainAliases =
mock(Map.class);
-
when(importChainAliases.get(topMostNamespace)).thenReturn(Collections.singletonList(chainedModels));
-
- populateContextWithInheritedData(toPopulate, toStore, importName,
topMostNamespace, importChainAliases);
- assertThat(toPopulate).usingRecursiveComparison().isEqualTo(context);
- }
-
- @Test
- void testProcessTopmostModelTuple() {
- DMNModelImpl model = mock(DMNModelImpl.class);
- DMNModelImpl.ModelImportTuple topmostModelTuple =
mock(DMNModelImpl.ModelImportTuple.class);
- DMNModelImpl topmostModel = mock(DMNModelImpl.class);
-
- DMNContext context = new DMNContextImpl();
- context.set("Person Name", "Klaus");
-
- InputData topmostInput = mock(InputData.class);
- InputDataNodeImpl node = new InputDataNodeImpl(topmostInput);
-
- when(topmostModelTuple.getModel()).thenReturn(topmostModel);
- when(topmostModel.getInputs()).thenReturn(Set.of(node));
- when(topmostInput.getName()).thenReturn("Person Name");
- when(topmostModelTuple.getImportName()).thenReturn("parentModel");
-
- String namespace = "test-namespace";
-
when(topmostModelTuple.getModel().getNamespace()).thenReturn(namespace);
-
- Map<String, Collection<List<String>>> importChainAliases = new
HashMap<>();
- List<String> chain = Arrays.asList("Child A", "parentModel");
- importChainAliases.put(namespace, Collections.singletonList(chain));
- when(model.getImportChainAliases()).thenReturn(importChainAliases);
-
- processTopmostModelTuple(context, topmostModelTuple, model);
-
- assertThat(context.get("Person Name")).isEqualTo("Klaus");
- }
-
- @Test
- void testUpdateContextMap() {
- DMNModelImpl topmostModel = mock(DMNModelImpl.class);
- String namespace = "namespace";
- String topmostModelImportName = "parentModel";
- String importingModelName = "Child A";
- when(topmostModel.getNamespace()).thenReturn(namespace);
- when(topmostModel.getName()).thenReturn(topmostModelImportName);
-
- String inputName = "Person Name";
- String storedValue = "Klaus";
-
- Map<String, Collection<List<String>>> importChainAliases = new
HashMap<>();
- // Order is from bottom - i.e. executed model - to topmost - i.e.
imported
- List<String> chain = Arrays.asList(importingModelName,
topmostModelImportName);
- importChainAliases.put(namespace, Collections.singletonList(chain));
-
- DMNContextImpl context = new DMNContextImpl();
-
- DMNModelImpl.ModelImportTuple topmostModelTuple =
- new DMNModelImpl.ModelImportTuple(topmostModelImportName,
topmostModel);
-
- updateContextMap(context, importChainAliases, topmostModelTuple,
inputName, storedValue);
-
- @SuppressWarnings("unchecked")
- Map<String, Object> childA = (Map<String, Object>)
context.get(importingModelName);
- @SuppressWarnings("unchecked")
- Map<String, Object> parentMap = (Map<String, Object>)
childA.get(topmostModelImportName);
- assertThat(parentMap).containsEntry(inputName, storedValue);
- }
-
-}
\ No newline at end of file
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/impl/DMNRuntimeUtilsTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/impl/DMNRuntimeUtilsTest.java
new file mode 100644
index 0000000000..fb19c68a80
--- /dev/null
+++
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/impl/DMNRuntimeUtilsTest.java
@@ -0,0 +1,461 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kie.dmn.core.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.IntStream;
+import org.junit.jupiter.api.Test;
+import org.kie.dmn.api.core.DMNContext;
+import org.kie.dmn.api.core.DMNType;
+import org.kie.dmn.api.core.ast.DMNNode;
+import org.kie.dmn.api.core.ast.InputDataNode;
+import org.kie.dmn.core.ast.InputDataNodeImpl;
+import org.kie.dmn.model.api.Definitions;
+import org.kie.dmn.model.api.InputData;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+class DMNRuntimeUtilsTest {
+
+ @Test
+ void populateResultContextWithTopmostParentsValues() {
+ Definitions defs = mock(Definitions.class);
+ when(defs.getNamespace()).thenReturn("ns1");
+ when(defs.getName()).thenReturn("model1");
+ DMNModelImpl model = spy(new DMNModelImpl(defs));
+
+ Definitions parentDefs = mock(Definitions.class);
+ when(parentDefs.getNamespace()).thenReturn("ns2");
+ when(parentDefs.getName()).thenReturn("parentModel");
+ DMNModelImpl parent = new DMNModelImpl(parentDefs);
+
+ InputDataNode inputNode = mock(InputDataNode.class);
+ when(inputNode.getName()).thenReturn("inputA");
+ when(inputNode.getModelNamespace()).thenReturn("ns2");
+ when(inputNode.getId()).thenReturn("idA");
+ parent.addInput(inputNode);
+
+ model.setImportAliasForNS("parentAlias", "ns2", "parentModel");
+ Set<DMNModelImpl.ModelImportTuple> topmostParents =
+ Set.of(new DMNModelImpl.ModelImportTuple("parentAlias",
parent));
+ doReturn(Optional.of(topmostParents)).when(model).getTopmostParents();
+
+ DMNContext context = new DMNContextImpl();
+ Map<String, Object> parentAliasMap = new HashMap<>();
+ parentAliasMap.put("inputA", "valueA");
+ context.set("parentAlias", parentAliasMap);
+
+ DMNModelImpl.ModelImportTuple tuple = new
DMNModelImpl.ModelImportTuple("parentModel", parent);
+ Optional<Set<DMNModelImpl.ModelImportTuple>> optionalTopmostModels =
Optional.of(Set.of(tuple));
+
when(DMNRuntimeUtils.getTopmostModel(model)).thenReturn(optionalTopmostModels);
+
+ Map<String, Collection<List<String>>> mockImportChainAliases = new
HashMap<>();
+ mockImportChainAliases.put("ns2", List.of(List.of("parentAlias")));
+ when(model.getImportChainAliases()).thenReturn(mockImportChainAliases);
+
+ DMNRuntimeUtils.populateResultContextWithTopmostParentsValues(context,
model);
+
+ DMNContext expectedContext = new DMNContextImpl();
+ expectedContext.set("parentAlias", Map.of("inputA", "valueA"));
+
+ assertThat(context)
+ .usingRecursiveComparison()
+ .isEqualTo(expectedContext);
+ }
+
+ @Test
+ void populateInputsFromTopmostModels() {
+ DMNModelImpl importingModel = mock(DMNModelImpl.class);
+ DMNContext context = new DMNContextImpl();
+ context.set("Person Name", "Klaus");
+
+ InputData mockInputData = mock(InputData.class);
+ when(mockInputData.getName()).thenReturn("Person Name");
+ InputDataNodeImpl node = new InputDataNodeImpl(mockInputData);
+
+ DMNModelImpl topmostModel = mock(DMNModelImpl.class);
+ when(topmostModel.getInputs()).thenReturn(Set.of(node));
+
+ String namespace = "test-namespace";
+ DMNModelImpl.ModelImportTuple tupleA =
mock(DMNModelImpl.ModelImportTuple.class);
+ when(tupleA.getModel()).thenReturn(topmostModel);
+ when(tupleA.getImportName()).thenReturn("parentModel");
+ when(tupleA.getModel().getNamespace()).thenReturn(namespace);
+
+ Map<String, Collection<List<String>>> importChainAliases = new
HashMap<>();
+ List<String> chain = Arrays.asList("Child A", "parentModel");
+ importChainAliases.put(namespace, Collections.singletonList(chain));
+
when(importingModel.getImportChainAliases()).thenReturn(importChainAliases);
+
+ Set<DMNModelImpl.ModelImportTuple> topmostModels = Set.of(tupleA);
+
+ DMNRuntimeUtils.populateInputsFromTopmostModel(context,
importingModel, topmostModels);
+
+ assertThat(context.get("Person Name")).isEqualTo("Klaus");
+ }
+
+ @Test
+ void processTopmostModelTuple() {
+ DMNModelImpl model = mock(DMNModelImpl.class);
+ DMNModelImpl.ModelImportTuple topmostModelTuple =
mock(DMNModelImpl.ModelImportTuple.class);
+ DMNModelImpl topmostModel = mock(DMNModelImpl.class);
+
+ DMNContext context = new DMNContextImpl();
+ context.set("Person Name", "Klaus");
+
+ InputData topmostInput = mock(InputData.class);
+ InputDataNodeImpl node = new InputDataNodeImpl(topmostInput);
+
+ when(topmostModelTuple.getModel()).thenReturn(topmostModel);
+ when(topmostModel.getInputs()).thenReturn(Set.of(node));
+ when(topmostInput.getName()).thenReturn("Person Name");
+ when(topmostModelTuple.getImportName()).thenReturn("parentModel");
+
+ String namespace = "test-namespace";
+
when(topmostModelTuple.getModel().getNamespace()).thenReturn(namespace);
+
+ Map<String, Collection<List<String>>> importChainAliases = new
HashMap<>();
+ List<String> chain = Arrays.asList("Child A", "parentModel");
+ importChainAliases.put(namespace, Collections.singletonList(chain));
+ when(model.getImportChainAliases()).thenReturn(importChainAliases);
+
+ DMNRuntimeUtils.processTopmostModelTuple(context, topmostModelTuple,
model);
+
+ assertThat(context.get("Person Name")).isEqualTo("Klaus");
+ }
+
+ @Test
+ void updateContextMap() {
+ DMNModelImpl topmostModel = mock(DMNModelImpl.class);
+ String namespace = "namespace";
+ String topmostModelImportName = "parentModel";
+ String importingModelName = "Child A";
+ when(topmostModel.getNamespace()).thenReturn(namespace);
+ when(topmostModel.getName()).thenReturn(topmostModelImportName);
+
+ String inputName = "Person Name";
+ String storedValue = "Klaus";
+
+ Map<String, Collection<List<String>>> importChainAliases = new
HashMap<>();
+ // Order is from bottom - i.e. executed model - to topmost - i.e.
imported
+ List<String> chain = Arrays.asList(importingModelName,
topmostModelImportName);
+ importChainAliases.put(namespace, Collections.singletonList(chain));
+
+ DMNContextImpl context = new DMNContextImpl();
+
+ DMNModelImpl.ModelImportTuple topmostModelTuple =
+ new DMNModelImpl.ModelImportTuple(topmostModelImportName,
topmostModel);
+
+ DMNRuntimeUtils.updateContextMap(context, importChainAliases,
topmostModelTuple, inputName, storedValue);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> childA = (Map<String, Object>)
context.get(importingModelName);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> parentMap = (Map<String, Object>)
childA.get(topmostModelImportName);
+ assertThat(parentMap).containsEntry(inputName, storedValue);
+ }
+
+ @Test
+ void populateContextWithInheritedData() {
+ DMNContext toPopulate = new DMNContextImpl();
+ toPopulate.set("Person Name", "Klaus");
+
+ List<String> chainedModels = List.of("Child A", "parentModel");
+ Map toStore = Map.of("Person Name", "Klaus");
+ String importName = "parentModel";
+ String topMostNamespace =
"https://www.apache.org/customnamespace/_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9";
+
+ DMNContext context = new DMNContextImpl();
+ Map<String, Object> parentModel = Map.of("Person Name", "Klaus");
+ Map<String, Object> childA = Map.of("parentModel", parentModel);
+ context.set("Person Name", "Klaus");
+ context.set("Child A", childA);
+
+ Map<String, Collection<List<String>>> importChainAliases =
mock(Map.class);
+
when(importChainAliases.get(topMostNamespace)).thenReturn(Collections.singletonList(chainedModels));
+
+ DMNRuntimeUtils.populateContextWithInheritedData(toPopulate, toStore,
importName, topMostNamespace,
+ importChainAliases);
+ assertThat(toPopulate).usingRecursiveComparison().isEqualTo(context);
+ }
+
+ @Test
+ void addNewMapToContext() {
+ DMNContext toPopulate = new DMNContextImpl();
+ String importName = "parentModel";
+ Map<String, Object> toStore = Map.of("Person Name", "Klaus");
+ String chainedModel = "chainedModel";
+ DMNRuntimeUtils.addNewMapToContext(toPopulate, importName, toStore,
chainedModel);
+
assertThat(toPopulate.get(chainedModel)).isNotNull().isInstanceOf(Map.class);
+ Map<String, Object> retrieved = (Map<String, Object>)
toPopulate.get(chainedModel);
+ assertThat(retrieved).containsEntry(importName, toStore);
+ }
+
+ @Test
+ void addValueInsideMap() {
+ Map<String, Object> map = new HashMap<>();
+ DMNRuntimeUtils.addValueInsideMap(map, "Person Name", "Klaus");
+ assertThat(map).containsEntry("Person Name", "Klaus");
+
+ map = Collections.unmodifiableMap(new HashMap<>());
+ DMNRuntimeUtils.addValueInsideMap(map, "Person Name", "Klaus");
+ assertThat(map).isEmpty();
+ }
+
+ @Test
+ void getTopmostModel() {
+ DMNModelImpl importingModel = mock(DMNModelImpl.class);
+ DMNModelImpl importedModel = mock(DMNModelImpl.class);
+
+ when(importedModel.getNamespace())
+
.thenReturn("http://www.trisotech.com/definitions/_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9");
+ when(importedModel.getName())
+ .thenReturn("ParentModel");
+
+ when(importingModel.getImportAliasFor(importedModel.getNamespace(),
importedModel.getName()))
+ .thenReturn(Optional.of("parentModel"));
+
+ DMNModelImpl.ModelImportTuple tuple =
+ new DMNModelImpl.ModelImportTuple("parentModel",
importedModel);
+ Optional<Set<DMNModelImpl.ModelImportTuple>> optionalTopmostModels =
Optional.of(Set.of(tuple));
+
+
when(importingModel.getTopmostParents()).thenReturn(optionalTopmostModels);
+
+ Optional<Set<DMNModelImpl.ModelImportTuple>> topmostModel =
DMNRuntimeUtils.getTopmostModel(importingModel);
+
+ assertThat(topmostModel).isPresent();
+ topmostModel.ifPresent(set ->
+ assertThat(set)
+
.extracting(DMNModelImpl.ModelImportTuple::getImportName)
+ .containsOnly("parentModel")
+ );
+ }
+
+ @Test
+ void coerceSingleItemCollectionToValueWithCollectionValue() {
+ String nestedValue = "value";
+ Object value = Collections.singletonList("value");
+ DMNType type = mock(DMNType.class);
+ when(type.isCollection()).thenReturn(false);
+ Object retrieved =
DMNRuntimeUtils.coerceSingleItemCollectionToValue(value, type);
+ assertThat(retrieved).isEqualTo(nestedValue);
+
+ when(type.isCollection()).thenReturn(true);
+ retrieved = DMNRuntimeUtils.coerceSingleItemCollectionToValue(value,
type);
+ assertThat(retrieved).isEqualTo(value);
+
+ value = Arrays.asList("value", "value2");
+ when(type.isCollection()).thenReturn(false);
+ retrieved = DMNRuntimeUtils.coerceSingleItemCollectionToValue(value,
type);
+ assertThat(retrieved).isEqualTo(value);
+
+ when(type.isCollection()).thenReturn(true);
+ retrieved = DMNRuntimeUtils.coerceSingleItemCollectionToValue(value,
type);
+ assertThat(retrieved).isEqualTo(value);
+ }
+
+ @Test
+ void coerceSingleItemCollectionToValueWithSingleValue() {
+ Object value = "value";
+ DMNType type = mock(DMNType.class);
+ when(type.isCollection()).thenReturn(false);
+ Object retrieved =
DMNRuntimeUtils.coerceSingleItemCollectionToValue(value, type);
+ assertThat(retrieved).isEqualTo(value);
+
+ when(type.isCollection()).thenReturn(true);
+ retrieved = DMNRuntimeUtils.coerceSingleItemCollectionToValue(value,
type);
+ assertThat(retrieved).isEqualTo(value);
+ }
+
+ @Test
+ void getObjectString() {
+ StringBuilder builder = new StringBuilder();
+ IntStream.range(0, 10).forEach(i ->
builder.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ String retrieved = DMNRuntimeUtils.getObjectString(builder.toString());
+ assertThat(retrieved).contains("[string clipped after 50 chars, total
length is 260]");
+
+ OverflowingObject overflowingObject = new
OverflowingObject("OVERFLOWING OBJECT");
+ retrieved = DMNRuntimeUtils.getObjectString(overflowingObject);
+ assertThat(retrieved).isEqualTo("(_undefined_)");
+ }
+
+ @Test
+ void getDependencyIdentifierSameNamespace() {
+ String namespace = "test-namespace";
+ String callerId = "callerId";
+ String callerName = "callerName";
+ DMNNode callerNode = mock(DMNNode.class);
+ when(callerNode.getName()).thenReturn(callerName);
+ when(callerNode.getId()).thenReturn(callerId);
+ when(callerNode.getModelNamespace()).thenReturn(namespace);
+
+ String calledId = "calledId";
+ String calledName = "calledName";
+ DMNNode calledNode = mock(DMNNode.class);
+ when(calledNode.getName()).thenReturn(calledName);
+ when(calledNode.getId()).thenReturn(calledId);
+ when(calledNode.getModelNamespace()).thenReturn(namespace);
+ assertThat(DMNRuntimeUtils.getDependencyIdentifier(callerNode,
calledNode)).isEqualTo(calledName);
+ }
+
+ @Test
+ void getDependencyIdentifierDifferentNamespace() {
+ String callerId = "callerId";
+ String callerName = "callerName";
+ String callerNamespace = "callerNamespace";
+ String alias = "alias";
+ Optional<String> importAlias = Optional.of(alias);
+ String calledId = "calledId";
+ String calledName = "calledName";
+ String calledNamespace = "calledNamespace";
+ String calledModelName = "calledModelName";
+
+ DMNNode callerNode = mock(DMNNode.class);
+ when(callerNode.getName()).thenReturn(callerName);
+ when(callerNode.getId()).thenReturn(callerId);
+ when(callerNode.getModelNamespace()).thenReturn(callerNamespace);
+ when(callerNode.getModelImportAliasFor(calledNamespace,
calledModelName)).thenReturn(importAlias);
+
+ DMNNode calledNode = mock(DMNNode.class);
+ when(calledNode.getName()).thenReturn(calledName);
+ when(calledNode.getId()).thenReturn(calledId);
+ when(calledNode.getModelNamespace()).thenReturn(calledNamespace);
+ when(calledNode.getModelName()).thenReturn(calledModelName);
+
+ String expected = alias + "." + calledName;
+ assertThat(DMNRuntimeUtils.getDependencyIdentifier(callerNode,
calledNode)).isEqualTo(expected);
+ }
+
+ @Test
+ void getDependencyIdentifierDifferentNamespaceWithoutAlias() {
+ String callerId = "callerId";
+ String callerName = "callerName";
+ String callerNamespace = "callerNamespace";
+ String calledId = "calledId";
+ String calledName = "calledName";
+ String calledNamespace = "calledNamespace";
+ String calledModelName = "calledModelName";
+
+ DMNNode callerNode = mock(DMNNode.class);
+ when(callerNode.getName()).thenReturn(callerName);
+ when(callerNode.getId()).thenReturn(callerId);
+ when(callerNode.getModelNamespace()).thenReturn(callerNamespace);
+ when(callerNode.getModelImportAliasFor(calledNamespace,
calledModelName)).thenReturn(Optional.empty());
+
+ DMNNode calledNode = mock(DMNNode.class);
+ when(calledNode.getName()).thenReturn(calledName);
+ when(calledNode.getId()).thenReturn(calledId);
+ when(calledNode.getModelNamespace()).thenReturn(calledNamespace);
+ when(calledNode.getModelName()).thenReturn(calledModelName);
+
+ String expected = "{" + calledNamespace + "}" + "." + calledName;
+ assertThat(DMNRuntimeUtils.getDependencyIdentifier(callerNode,
calledNode)).isEqualTo(expected);
+ }
+
+ @Test
+ void getPrefixedIdentifierWithAlias() {
+ String callerId = "callerId";
+ String callerName = "callerName";
+ String callerNamespace = "callerNamespace";
+ String alias = "alias";
+ Optional<String> importAlias = Optional.of(alias);
+ String calledId = "calledId";
+ String calledName = "calledName";
+ String calledNamespace = "calledNamespace";
+ String calledModelName = "calledModelName";
+
+ DMNNode callerNode = mock(DMNNode.class);
+ when(callerNode.getName()).thenReturn(callerName);
+ when(callerNode.getId()).thenReturn(callerId);
+ when(callerNode.getModelNamespace()).thenReturn(callerNamespace);
+ when(callerNode.getModelImportAliasFor(calledNamespace,
calledModelName)).thenReturn(importAlias);
+
+ DMNNode calledNode = mock(DMNNode.class);
+ when(calledNode.getName()).thenReturn(calledName);
+ when(calledNode.getId()).thenReturn(calledId);
+ when(calledNode.getModelNamespace()).thenReturn(calledNamespace);
+ when(calledNode.getModelName()).thenReturn(calledModelName);
+
+ String expected = alias + "." + calledName;
+ assertThat(DMNRuntimeUtils.getPrefixedIdentifier(callerNode,
calledNode)).isEqualTo(expected);
+ }
+
+ @Test
+ void getPrefixedIdentifierWithoutAlias() {
+ String callerId = "callerId";
+ String callerName = "callerName";
+ String callerNamespace = "callerNamespace";
+ String calledId = "calledId";
+ String calledName = "calledName";
+ String calledNamespace = "calledNamespace";
+ String calledModelName = "calledModelName";
+
+ DMNNode callerNode = mock(DMNNode.class);
+ when(callerNode.getName()).thenReturn(callerName);
+ when(callerNode.getId()).thenReturn(callerId);
+ when(callerNode.getModelNamespace()).thenReturn(callerNamespace);
+ when(callerNode.getModelImportAliasFor(calledNamespace,
calledModelName)).thenReturn(Optional.empty());
+
+ DMNNode calledNode = mock(DMNNode.class);
+ when(calledNode.getName()).thenReturn(calledName);
+ when(calledNode.getId()).thenReturn(calledId);
+ when(calledNode.getModelNamespace()).thenReturn(calledNamespace);
+ when(calledNode.getModelName()).thenReturn(calledModelName);
+
+ String expected = "{" + calledNamespace + "}" + "." + calledName;
+ assertThat(DMNRuntimeUtils.getPrefixedIdentifier(callerNode,
calledNode)).isEqualTo(expected);
+ }
+
+ @Test
+ void getIdentifier() {
+ String id = "id";
+ String name = "name";
+ DMNNode dmnNode = mock(DMNNode.class);
+ when(dmnNode.getName()).thenReturn(null);
+ when(dmnNode.getId()).thenReturn(id);
+ assertThat(DMNRuntimeUtils.getIdentifier(dmnNode)).isEqualTo(id);
+ when(dmnNode.getName()).thenReturn(name);
+ assertThat(DMNRuntimeUtils.getIdentifier(dmnNode)).isEqualTo(name);
+ }
+
+ private static class OverflowingObject {
+
+ String string;
+
+ public OverflowingObject(String string) {
+ this.string = string;
+ }
+
+ @Override
+ public String toString() {
+ return this.toString();
+ }
+ }
+}
\ No newline at end of file
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/imports/ImportsTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/imports/ImportsTest.java
index dd92e6311c..243375e944 100644
---
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/imports/ImportsTest.java
+++
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/imports/ImportsTest.java
@@ -21,6 +21,7 @@ package org.kie.dmn.core.imports;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Map;
import java.util.function.Function;
import org.junit.jupiter.params.ParameterizedTest;
@@ -44,6 +45,97 @@ public class ImportsTest extends
BaseInterpretedVsCompiledTest {
public static final Logger LOG =
LoggerFactory.getLogger(ImportsTest.class);
+ @ParameterizedTest
+ @MethodSource("params")
+ void importingModelsWithSameImportAndInputNameSimpleData(boolean
useExecModelCompiler) {
+ init(useExecModelCompiler);
+ String basePath =
"valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data";
+ String dmnImporting = String.format("%s/ImportingModel.dmn", basePath);
+ String dmnImported = String.format("%s/ImportedModel.dmn", basePath);
+ final DMNRuntime runtime =
DMNRuntimeUtil.createRuntimeWithAdditionalResources(dmnImporting,
+
this.getClass(),
+
dmnImported);
+
+ final DMNModel importedModel =
runtime.getModel("https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A7837",
+
"DMN_C66F78A2-4BD7-4BB7-A200-4C61D82AAFBD");
+ assertThat(importedModel).isNotNull();
+
assertThat(importedModel.hasErrors()).as(DMNRuntimeUtil.formatMessages(importedModel.getMessages())).isFalse();
+
+ final DMNModel importingModel =
runtime.getModel("https://kie.org/dmn/_054766E3-2098-4E7B-8F48-576B5373F4EE",
+
"ImportingModelSimpleType");
+ assertThat(importingModel).isNotNull();
+
assertThat(importingModel.hasErrors()).as(DMNRuntimeUtil.formatMessages(importingModel.getMessages())).isFalse();
+
+ DMNContext context = runtime.newContext(); // old syntax
+ context.set("My Input", "this is my input");
+ context.set("CLASHING_NAME", mapOf(entry("CLASHING_NAME", 21)));
+
+ DMNResult evaluateAll = runtime.evaluateAll(importingModel, context);
+
assertThat(evaluateAll.hasErrors()).as(DMNRuntimeUtil.formatMessages(evaluateAll.getMessages())).isFalse();
+
+ LOG.debug("{}", evaluateAll);
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision")).isNotNull();
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision").getResult()).isEqualTo("this is my input: 21");
+
+
+ context = runtime.newContext();
+ context.set("My Input", "this is my input");
+ context.set("CLASHING_NAME", 21);
+
+ evaluateAll = runtime.evaluateAll(importingModel, context);
+
assertThat(evaluateAll.hasErrors()).as(DMNRuntimeUtil.formatMessages(evaluateAll.getMessages())).isFalse();
+
+ LOG.debug("{}", evaluateAll);
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision")).isNotNull();
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision").getResult()).isEqualTo("this is my input: 21");
+ }
+
+ @ParameterizedTest
+ @MethodSource("params")
+ void importingModelsWithSameImportAndInputNameComplexData(boolean
useExecModelCompiler) {
+ init(useExecModelCompiler);
+ String basePath =
"valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data";
+ String dmnImporting = String.format("%s/ImportingModel.dmn", basePath);
+ String dmnImported = String.format("%s/ImportedModel.dmn", basePath);
+ final DMNRuntime runtime =
DMNRuntimeUtil.createRuntimeWithAdditionalResources(dmnImporting,
+
this.getClass(),
+
dmnImported);
+
+ final DMNModel importedModel =
runtime.getModel("https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A78EE",
+
"DMN_C66F78A2-4BD7-4BB7-A200-4C61D82AAFBD");
+ assertThat(importedModel).isNotNull();
+
assertThat(importedModel.hasErrors()).as(DMNRuntimeUtil.formatMessages(importedModel.getMessages())).isFalse();
+
+ final DMNModel importingModel =
runtime.getModel("https://kie.org/dmn/_054766E3-2098-4E7B-8F48-576B5373F4EE",
+
"ImportingModelComplexType");
+ assertThat(importingModel).isNotNull();
+
assertThat(importingModel.hasErrors()).as(DMNRuntimeUtil.formatMessages(importingModel.getMessages())).isFalse();
+
+ DMNContext context = runtime.newContext(); // old syntax
+ context.set("My Input", "this is my input");
+ Map<String, Object> clashingNameComplexObject = mapOf(entry("Complex
Type Number", 21), entry("Complex Type String", "A string"));
+ context.set("CLASHING_NAME", mapOf(entry("CLASHING_NAME",
clashingNameComplexObject)));
+
+ DMNResult evaluateAll = runtime.evaluateAll(importingModel, context);
+
assertThat(evaluateAll.hasErrors()).as(DMNRuntimeUtil.formatMessages(evaluateAll.getMessages())).isFalse();
+
+ LOG.debug("{}", evaluateAll);
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision")).isNotNull();
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision").getResult()).isEqualTo("this is my input: 21");
+
+
+ context = runtime.newContext();
+ context.set("My Input", "this is my input");
+ context.set("CLASHING_NAME", mapOf(entry("Complex Type Number", 21),
entry("Complex Type String", "A string")));
+
+ evaluateAll = runtime.evaluateAll(importingModel, context);
+
assertThat(evaluateAll.hasErrors()).as(DMNRuntimeUtil.formatMessages(evaluateAll.getMessages())).isFalse();
+
+ LOG.debug("{}", evaluateAll);
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision")).isNotNull();
+ assertThat(evaluateAll.getDecisionResultByName("My
Decision").getResult()).isEqualTo("this is my input: 21");
+ }
+
@ParameterizedTest
@MethodSource("params")
void importingOverridedInputDataInInheritedDecision(boolean
useExecModelCompiler) {
diff --git
a/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_6/InvalidModelImportInputDataNameClash.dmn
b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_6/InvalidModelImportInputDataNameClash.dmn
new file mode 100644
index 0000000000..b857cfefde
--- /dev/null
+++
b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_6/InvalidModelImportInputDataNameClash.dmn
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+-->
+<definitions xmlns="https://www.omg.org/spec/DMN/20240513/MODEL/"
expressionLanguage="https://www.omg.org/spec/DMN/20240513/FEEL/"
namespace="https://kie.org/dmn/_CB218CE4-3C32-44EC-986B-2509FD2DB804"
id="_AE7757F5-3E9F-4306-9498-4F777597ECB1"
name="DMN_F57A0F3D-06A9-4591-AF2F-D4A1CDB33D00"
xmlns:included0="https://kie.org/dmn/_19017424-534B-4943-9DD3-3B5C71E64554"
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" xmlns:di="htt [...]
+ <import id="_5B34C986-A41B-4530-88CE-2ECDC0328F69" name="CLASHING_NAME"
importType="https://www.omg.org/spec/DMN/20240513/MODEL/"
namespace="http://www.trisotech.com/definitions/_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9"
locationURI="./ParentModel.dmn" />
+ <decision name="InvalidDecision" id="_4570A6F0-A31F-456B-B73C-A82F207AB645">
+ <variable name="InvalidDecision"
id="_CDD34C93-2771-4627-BEDB-0130B8AB6968" />
+ <informationRequirement id="_01C626D2-8CC3-4B2E-8647-17D5920AE8F1">
+ <requiredInput
href="http://www.trisotech.com/definitions/_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9#_4f6c136c-8512-4d71-8bbf-7c9eb6e74063"
/>
+ </informationRequirement>
+ <literalExpression id="_1D8D0D0B-80F9-49D3-8C00-E9A82574413D"
label="InvalidDecision" typeRef="string">
+ <text>"Hello"</text>
+ </literalExpression>
+ </decision>
+ <inputData name="CLASHING_NAME" id="_BF9A3642-A804-4175-99B1-702C099F26AC">
+ <variable name="CLASHING_NAME" id="_FF16066D-357E-4F89-A7FE-44E36941EAAA"
typeRef="number" />
+ </inputData>
+ <dmndi:DMNDI>
+ <dmndi:DMNDiagram id="_11C7251C-0054-4571-BFBD-663E56C5989F" name="Default
DRD" useAlternativeInputDataShape="false">
+ <di:extension>
+ <kie:ComponentsWidthsExtension>
+ <kie:ComponentWidths
dmnElementRef="_1D8D0D0B-80F9-49D3-8C00-E9A82574413D">
+ <kie:width>190</kie:width>
+ </kie:ComponentWidths>
+ </kie:ComponentsWidthsExtension>
+ </di:extension>
+ <dmndi:DMNShape id="_61FD3C47-BBA8-460E-8E0A-8CE9A94EAA18"
dmnElementRef="included0:_D93251BA-B455-4041-9148-4491E3FBFC76">
+ <dc:Bounds x="360" y="140" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_76BC9E04-F772-4BCD-A564-B25673B1A189"
dmnElementRef="_4570A6F0-A31F-456B-B73C-A82F207AB645" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="600" y="140" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNEdge id="_BDE31C3B-6FE5-4A4B-9375-77D25DB4939D"
dmnElementRef="_01C626D2-8CC3-4B2E-8647-17D5920AE8F1"
sourceElement="_61FD3C47-BBA8-460E-8E0A-8CE9A94EAA18"
targetElement="_76BC9E04-F772-4BCD-A564-B25673B1A189">
+ <di:waypoint x="440" y="180" />
+ <di:waypoint x="600" y="180" />
+ </dmndi:DMNEdge>
+ </dmndi:DMNDiagram>
+ </dmndi:DMNDI>
+</definitions>
diff --git
a/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_6/ParentModel.dmn
b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_6/ParentModel.dmn
new file mode 100644
index 0000000000..54aba9c4a7
--- /dev/null
+++
b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_6/ParentModel.dmn
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<definitions xmlns:semantic="https://www.omg.org/spec/DMN/20240513/MODEL/"
+
xmlns:dmn11="https://www.omg.org/spec/DMN/20230324/DMN15.xsd"
+ xmlns:rss="http://purl.org/rss/2.0/"
+ xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"
+ xmlns:trisofeed="http://trisotech.com/feed"
+
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"
+ xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
+
xmlns:triso="http://www.trisotech.com/2015/triso/modeling"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:feel="https://www.omg.org/spec/DMN/20240513/FEEL/"
+ xmlns:trisodmn="http://www.trisotech.com/2016/triso/dmn"
+ xmlns:tc="http://www.omg.org/spec/DMN/20160719/testcase"
+ xmlns:drools="http://www.drools.org/kie/dmn/1.1"
+ xmlns="https://www.omg.org/spec/DMN/20240513/MODEL/"
+ id="_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9"
name="ParentModel"
+
namespace="http://www.trisotech.com/definitions/_ae5b3c17-1ac3-4e1d-b4f9-2cf861aec6d9"
exporter="DMN Modeler" exporterVersion="6.2.4.2" triso:logoChoice="Default">
+ <inputData id="_4f6c136c-8512-4d71-8bbf-7c9eb6e74063" name="Person name">
+ <variable name="Person name"
id="_46b6677b-4a26-4bca-9532-9a57dd55b8ec" typeRef="string"/>
+ </inputData>
+ <decision id="_f7fdaec4-d669-4797-b3b4-12b860de2eb5" name="Greet the
Person" triso:useOutputTypeAsAnswer="false">
+ <variable name="Greet the Person"
id="_85193e88-cb32-41da-9181-fb8e5450753a" typeRef="string"/>
+ <informationRequirement id="b1507384-44a9-4da7-8223-fa49ffa65410">
+ <requiredInput href="#_4f6c136c-8512-4d71-8bbf-7c9eb6e74063"/>
+ </informationRequirement>
+ <literalExpression typeRef="string"
id="_429b0c63-31e0-4f79-b457-32f565167702"
triso:expressionId="_28c34a9a-ebce-4ae4-ae8e-7eb062aa35d3">
+ <text>"Hello, "+Person name</text>
+ </literalExpression>
+ </decision>
+</definitions>
diff --git
a/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data/ImportedModel.dmn
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data/ImportedModel.dmn
new file mode 100644
index 0000000000..481b163b6e
--- /dev/null
+++
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data/ImportedModel.dmn
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"
+ xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"
xmlns:kie="https://kie.org/dmn/extensions/1.0"
+
xmlns:included0="https://kie.org/dmn/_9F4C1A60-CF94-41F1-9B9F-8D26FF617670"
xmlns:included1="https://kie.org/dmn/_D9035A17-CCDF-4D29-9A4C-005D2BF5EBDC"
+ expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/"
namespace="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A78EE"
id="_AF126883-5B66-4112-A49C-FBC9D85E9F4D"
+ name="DMN_C66F78A2-4BD7-4BB7-A200-4C61D82AAFBD">
+ <itemDefinition id="_complexType" name="ComplexType">
+ <itemComponent id="_complexTypeNumber" name="Complex Type Number">
+ <typeRef>number</typeRef>
+ </itemComponent>
+ <itemComponent id="_complexTypeString" name="Complex Type String">
+ <typeRef>string</typeRef>
+ </itemComponent>
+ </itemDefinition>
+ <decision name="Decision A" id="_A98A15BF-9E3E-4DBC-874C-E722A1DF082D">
+ <variable id="_D723085A-ED61-4A87-8933-65B5281A567D" typeRef="number"
name="Decision A"/>
+ <informationRequirement id="_D4E08E55-7035-420D-9B8F-74EE9EB6B092">
+ <requiredInput href="#_C299FD32-7DA5-44F5-959E-A23470AA4F3F"/>
+ </informationRequirement>
+ <literalExpression id="_28293632-A28E-47D8-B25E-A7C934ED4537"
typeRef="number" label="Decision A">
+ <text>CLASHING_NAME.Complex Type Number</text>
+ </literalExpression>
+ </decision>
+ <inputData name="CLASHING_NAME" id="_C299FD32-7DA5-44F5-959E-A23470AA4F3F">
+ <variable name="CLASHING_NAME" id="_6497C8B9-F48A-42D4-B299-534C12E45190"
typeRef="ComplexType"/>
+ </inputData>
+ <dmndi:DMNDI>
+ <dmndi:DMNDiagram id="_EC7829BB-AA43-4C41-8CBD-02799F8E5CA2" name="Default
DRD" useAlternativeInputDataShape="false">
+ <di:extension>
+ <kie:ComponentsWidthsExtension>
+ <kie:ComponentWidths
dmnElementRef="_C7A7A9AA-CB35-4A90-ACCA-BAC1835410AF">
+ <kie:width>190</kie:width>
+ </kie:ComponentWidths>
+ <kie:ComponentWidths
dmnElementRef="_E706443A-D7B7-4E86-A984-423B02CE91C3">
+ <kie:width>190</kie:width>
+ </kie:ComponentWidths>
+ <kie:ComponentWidths
dmnElementRef="_AF9208AE-A31D-48C7-953F-D409C5781C4D">
+ <kie:width>512</kie:width>
+ </kie:ComponentWidths>
+ <kie:ComponentWidths
dmnElementRef="_28293632-A28E-47D8-B25E-A7C934ED4537">
+ <kie:width>190</kie:width>
+ </kie:ComponentWidths>
+ </kie:ComponentsWidthsExtension>
+ </di:extension>
+ <dmndi:DMNShape id="_E5978038-2A5F-4BE5-A5F8-83E41802AF47"
dmnElementRef="_C299FD32-7DA5-44F5-959E-A23470AA4F3F" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="160" y="260" width="160" height="80"/>
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_B142BA3C-808B-4BF0-98F1-EF003063D1D9"
dmnElementRef="included0:_333AB4CE-C1BC-49BD-84B8-E6A2339C9E8D">
+ <dc:Bounds x="460" y="180" width="160" height="80"/>
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_7A990C14-71C7-4405-8EE3-9FCEA81B28B7"
dmnElementRef="_A98A15BF-9E3E-4DBC-874C-E722A1DF082D" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="-280" y="60" width="160" height="80"/>
+ </dmndi:DMNShape>
+ <dmndi:DMNEdge id="_101FB2F5-7F7E-45BC-A4E4-F80FC2CB898E-AUTO-TARGET"
dmnElementRef="_D4E08E55-7035-420D-9B8F-74EE9EB6B092"
sourceElement="_D7639B3F-D677-4B3E-BBA7-D6D1077782DC"
targetElement="_7A990C14-71C7-4405-8EE3-9FCEA81B28B7">
+ <di:waypoint x="-200" y="300"/>
+ <di:waypoint x="-200" y="100"/>
+ </dmndi:DMNEdge>
+ </dmndi:DMNDiagram>
+ </dmndi:DMNDI>
+</definitions>
diff --git
a/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data/ImportingModel.dmn
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data/ImportingModel.dmn
new file mode 100644
index 0000000000..f3ae1e69b7
--- /dev/null
+++
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data/ImportingModel.dmn
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
+ xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"
xmlns:kie="https://kie.org/dmn/extensions/1.0"
xmlns:included1="https://kie.org/dmn/_9B908A38-2808-4AA6-8387-5A0063159BFD"
+
xmlns:included0="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A78EE"
expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/"
+
namespace="https://kie.org/dmn/_054766E3-2098-4E7B-8F48-576B5373F4EE"
id="_EC6DEDC6-F33D-4058-8C6C-7FAF676BB0FF"
+ name="ImportingModelComplexType">
+ <import id="_903A7C44-32F0-4453-B0A6-77216FF872D2" name="CLASHING_NAME"
importType="https://www.omg.org/spec/DMN/20230324/MODEL/"
namespace="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A78EE"
locationURI="./ImportedModel.dmn" />
+ <decision name="My Decision" id="_27D3FDE5-2DF4-4BCD-8224-4FB16CC6731E">
+ <variable id="_98824C72-1DAC-4C04-84DF-8908AD40910F" name="My Decision"
typeRef="string" />
+ <informationRequirement id="_46788B87-3143-4D6C-B05C-7C0B6B314256">
+ <requiredInput href="#_5BF60A32-996A-49EE-A041-08D0F886CB08" />
+ </informationRequirement>
+ <informationRequirement id="_7F9763EA-687A-4841-AFF5-03DA1DE2E099">
+ <requiredDecision
href="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A78EE#_A98A15BF-9E3E-4DBC-874C-E722A1DF082D"
/>
+ </informationRequirement>
+ <literalExpression id="_1F1BC8D3-853D-48CA-80B3-4A65A87A3F76"
typeRef="string" label="My Decision">
+ <text>My Input + ": " + string(CLASHING_NAME.Decision A)
</text>
+ </literalExpression>
+ </decision>
+ <inputData name="My Input" id="_5BF60A32-996A-49EE-A041-08D0F886CB08">
+ <variable name="My Input" id="_5868E018-1C72-45A8-9CF0-665E42016411"
typeRef="string" />
+ </inputData>
+ <dmndi:DMNDI>
+ <dmndi:DMNDiagram id="_A8B37C28-E077-4160-A9D0-A6BF5B374D23" name="Default
DRD" useAlternativeInputDataShape="false">
+ <di:extension>
+ <kie:ComponentsWidthsExtension>
+ <kie:ComponentWidths
dmnElementRef="_1F1BC8D3-853D-48CA-80B3-4A65A87A3F76">
+ <kie:width>434</kie:width>
+ </kie:ComponentWidths>
+ </kie:ComponentsWidthsExtension>
+ </di:extension>
+ <dmndi:DMNShape id="_A6171152-8E16-44D0-BCF5-87C0BCD7BA54"
dmnElementRef="_5BF60A32-996A-49EE-A041-08D0F886CB08" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="220" y="320" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_5964FAD5-5E3E-402A-AE78-8D20114FD241"
dmnElementRef="_27D3FDE5-2DF4-4BCD-8224-4FB16CC6731E" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="220" y="120" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_326986E1-A1CE-4A7A-94D1-8815908E7BC3"
dmnElementRef="included0:_5DD887C6-45B9-4EE7-850F-C50FF1579258">
+ <dc:Bounds x="500" y="200" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNEdge id="_5B9A45AD-F65D-4445-8D88-9250C012B4CA-AUTO-TARGET"
dmnElementRef="_46788B87-3143-4D6C-B05C-7C0B6B314256"
sourceElement="_A6171152-8E16-44D0-BCF5-87C0BCD7BA54"
targetElement="_5964FAD5-5E3E-402A-AE78-8D20114FD241">
+ <di:waypoint x="300" y="360" />
+ <di:waypoint x="300" y="160" />
+ </dmndi:DMNEdge>
+ <dmndi:DMNEdge id="_31AE5339-F989-4241-80CD-8988B356E162"
dmnElementRef="_7F9763EA-687A-4841-AFF5-03DA1DE2E099"
sourceElement="_326986E1-A1CE-4A7A-94D1-8815908E7BC3"
targetElement="_5964FAD5-5E3E-402A-AE78-8D20114FD241">
+ <di:waypoint x="580" y="240" />
+ <di:waypoint x="380" y="160" />
+ </dmndi:DMNEdge>
+ </dmndi:DMNDiagram>
+ </dmndi:DMNDI>
+</definitions>
diff --git
a/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data/ImportedModel.dmn
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data/ImportedModel.dmn
new file mode 100644
index 0000000000..6200a4f631
--- /dev/null
+++
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data/ImportedModel.dmn
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"
+ xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"
xmlns:kie="https://kie.org/dmn/extensions/1.0"
+
xmlns:included0="https://kie.org/dmn/_9F4C1A60-CF94-41F1-9B9F-8D26FF617670"
xmlns:included1="https://kie.org/dmn/_D9035A17-CCDF-4D29-9A4C-005D2BF5EBDC"
+ expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/"
namespace="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A7837"
id="_AF126883-5B66-4112-A49C-FBC9D85E9F4D"
+ name="DMN_C66F78A2-4BD7-4BB7-A200-4C61D82AAFBD">
+ <decision name="Decision A" id="_A98A15BF-9E3E-4DBC-874C-E722A1DF082D">
+ <variable id="_D723085A-ED61-4A87-8933-65B5281A567D" typeRef="number"
name="Decision A" />
+ <informationRequirement id="_D4E08E55-7035-420D-9B8F-74EE9EB6B092">
+ <requiredInput href="#_C299FD32-7DA5-44F5-959E-A23470AA4F3F" />
+ </informationRequirement>
+ <literalExpression id="_28293632-A28E-47D8-B25E-A7C934ED4537"
typeRef="number" label="Decision A">
+ <text>CLASHING_NAME</text>
+ </literalExpression>
+ </decision>
+ <inputData name="CLASHING_NAME" id="_C299FD32-7DA5-44F5-959E-A23470AA4F3F">
+ <variable name="CLASHING_NAME" id="_6497C8B9-F48A-42D4-B299-534C12E45190"
typeRef="number" />
+ </inputData>
+ <dmndi:DMNDI>
+ <dmndi:DMNDiagram id="_EC7829BB-AA43-4C41-8CBD-02799F8E5CA2" name="Default
DRD" useAlternativeInputDataShape="false">
+ <di:extension>
+ <kie:ComponentsWidthsExtension>
+ <kie:ComponentWidths
dmnElementRef="_C7A7A9AA-CB35-4A90-ACCA-BAC1835410AF">
+ <kie:width>190</kie:width>
+ </kie:ComponentWidths>
+ <kie:ComponentWidths
dmnElementRef="_E706443A-D7B7-4E86-A984-423B02CE91C3">
+ <kie:width>190</kie:width>
+ </kie:ComponentWidths>
+ <kie:ComponentWidths
dmnElementRef="_AF9208AE-A31D-48C7-953F-D409C5781C4D">
+ <kie:width>512</kie:width>
+ </kie:ComponentWidths>
+ <kie:ComponentWidths
dmnElementRef="_28293632-A28E-47D8-B25E-A7C934ED4537">
+ <kie:width>190</kie:width>
+ </kie:ComponentWidths>
+ </kie:ComponentsWidthsExtension>
+ </di:extension>
+ <dmndi:DMNShape id="_E5978038-2A5F-4BE5-A5F8-83E41802AF47"
dmnElementRef="_C299FD32-7DA5-44F5-959E-A23470AA4F3F" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="160" y="260" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_B142BA3C-808B-4BF0-98F1-EF003063D1D9"
dmnElementRef="included0:_333AB4CE-C1BC-49BD-84B8-E6A2339C9E8D">
+ <dc:Bounds x="460" y="180" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_7A990C14-71C7-4405-8EE3-9FCEA81B28B7"
dmnElementRef="_A98A15BF-9E3E-4DBC-874C-E722A1DF082D" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="-280" y="60" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNEdge id="_101FB2F5-7F7E-45BC-A4E4-F80FC2CB898E-AUTO-TARGET"
dmnElementRef="_D4E08E55-7035-420D-9B8F-74EE9EB6B092"
sourceElement="_D7639B3F-D677-4B3E-BBA7-D6D1077782DC"
targetElement="_7A990C14-71C7-4405-8EE3-9FCEA81B28B7">
+ <di:waypoint x="-200" y="300" />
+ <di:waypoint x="-200" y="100" />
+ </dmndi:DMNEdge>
+ </dmndi:DMNDiagram>
+ </dmndi:DMNDI>
+</definitions>
diff --git
a/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data/ImportingModel.dmn
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data/ImportingModel.dmn
new file mode 100644
index 0000000000..e3128f06f7
--- /dev/null
+++
b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data/ImportingModel.dmn
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
+ xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"
xmlns:kie="https://kie.org/dmn/extensions/1.0"
xmlns:included1="https://kie.org/dmn/_9B908A38-2808-4AA6-8387-5A0063159BFD"
+
xmlns:included0="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A7837"
expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/"
+
namespace="https://kie.org/dmn/_054766E3-2098-4E7B-8F48-576B5373F4EE"
id="_EC6DEDC6-F33D-4058-8C6C-7FAF676BB0FF"
+ name="ImportingModelSimpleType">
+ <import id="_903A7C44-32F0-4453-B0A6-77216FF872D2" name="CLASHING_NAME"
importType="https://www.omg.org/spec/DMN/20230324/MODEL/"
namespace="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A7837"
locationURI="./ImportedModel.dmn" />
+ <decision name="My Decision" id="_27D3FDE5-2DF4-4BCD-8224-4FB16CC6731E">
+ <variable id="_98824C72-1DAC-4C04-84DF-8908AD40910F" name="My Decision"
typeRef="string" />
+ <informationRequirement id="_46788B87-3143-4D6C-B05C-7C0B6B314256">
+ <requiredInput href="#_5BF60A32-996A-49EE-A041-08D0F886CB08" />
+ </informationRequirement>
+ <informationRequirement id="_7F9763EA-687A-4841-AFF5-03DA1DE2E099">
+ <requiredDecision
href="https://kie.org/dmn/_EDBCAD2C-5119-462A-8ACA-7E32C18A7837#_A98A15BF-9E3E-4DBC-874C-E722A1DF082D"
/>
+ </informationRequirement>
+ <literalExpression id="_1F1BC8D3-853D-48CA-80B3-4A65A87A3F76"
typeRef="string" label="My Decision">
+ <text>My Input + ": " + string(CLASHING_NAME.Decision A)
</text>
+ </literalExpression>
+ </decision>
+ <inputData name="My Input" id="_5BF60A32-996A-49EE-A041-08D0F886CB08">
+ <variable name="My Input" id="_5868E018-1C72-45A8-9CF0-665E42016411"
typeRef="string" />
+ </inputData>
+ <dmndi:DMNDI>
+ <dmndi:DMNDiagram id="_A8B37C28-E077-4160-A9D0-A6BF5B374D23" name="Default
DRD" useAlternativeInputDataShape="false">
+ <di:extension>
+ <kie:ComponentsWidthsExtension>
+ <kie:ComponentWidths
dmnElementRef="_1F1BC8D3-853D-48CA-80B3-4A65A87A3F76">
+ <kie:width>434</kie:width>
+ </kie:ComponentWidths>
+ </kie:ComponentsWidthsExtension>
+ </di:extension>
+ <dmndi:DMNShape id="_A6171152-8E16-44D0-BCF5-87C0BCD7BA54"
dmnElementRef="_5BF60A32-996A-49EE-A041-08D0F886CB08" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="220" y="320" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_5964FAD5-5E3E-402A-AE78-8D20114FD241"
dmnElementRef="_27D3FDE5-2DF4-4BCD-8224-4FB16CC6731E" isCollapsed="false"
isListedInputData="false">
+ <dc:Bounds x="220" y="120" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="_326986E1-A1CE-4A7A-94D1-8815908E7BC3"
dmnElementRef="included0:_5DD887C6-45B9-4EE7-850F-C50FF1579258">
+ <dc:Bounds x="500" y="200" width="160" height="80" />
+ </dmndi:DMNShape>
+ <dmndi:DMNEdge id="_5B9A45AD-F65D-4445-8D88-9250C012B4CA-AUTO-TARGET"
dmnElementRef="_46788B87-3143-4D6C-B05C-7C0B6B314256"
sourceElement="_A6171152-8E16-44D0-BCF5-87C0BCD7BA54"
targetElement="_5964FAD5-5E3E-402A-AE78-8D20114FD241">
+ <di:waypoint x="300" y="360" />
+ <di:waypoint x="300" y="160" />
+ </dmndi:DMNEdge>
+ <dmndi:DMNEdge id="_31AE5339-F989-4241-80CD-8988B356E162"
dmnElementRef="_7F9763EA-687A-4841-AFF5-03DA1DE2E099"
sourceElement="_326986E1-A1CE-4A7A-94D1-8815908E7BC3"
targetElement="_5964FAD5-5E3E-402A-AE78-8D20114FD241">
+ <di:waypoint x="580" y="240" />
+ <di:waypoint x="380" y="160" />
+ </dmndi:DMNEdge>
+ </dmndi:DMNDiagram>
+ </dmndi:DMNDI>
+</definitions>
diff --git
a/kie-dmn/kie-dmn-validation/src/test/java/org/kie/dmn/validation/ValidatorTest.java
b/kie-dmn/kie-dmn-validation/src/test/java/org/kie/dmn/validation/ValidatorTest.java
index 997f5346c1..6f15423230 100644
---
a/kie-dmn/kie-dmn-validation/src/test/java/org/kie/dmn/validation/ValidatorTest.java
+++
b/kie-dmn/kie-dmn-validation/src/test/java/org/kie/dmn/validation/ValidatorTest.java
@@ -22,10 +22,12 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -39,12 +41,16 @@ import org.drools.io.ClassPathResource;
import org.drools.io.FileSystemResource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.kie.api.builder.Message;
import org.kie.api.builder.Message.Level;
import org.kie.api.io.Resource;
+import org.kie.dmn.api.core.DMNContext;
import org.kie.dmn.api.core.DMNMessage;
import org.kie.dmn.api.core.DMNMessageType;
import org.kie.dmn.api.core.DMNModel;
+import org.kie.dmn.api.core.DMNResult;
import org.kie.dmn.api.core.DMNRuntime;
import org.kie.dmn.api.marshalling.DMNMarshaller;
import org.kie.dmn.backend.marshalling.v1x.DMNMarshallerFactory;
@@ -63,6 +69,8 @@ import org.slf4j.LoggerFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
+import static org.kie.dmn.core.util.DynamicTypeUtils.entry;
+import static org.kie.dmn.core.util.DynamicTypeUtils.mapOf;
import static
org.kie.dmn.validation.DMNValidator.Validation.VALIDATE_COMPILATION;
import static org.kie.dmn.validation.DMNValidator.Validation.VALIDATE_MODEL;
import static org.kie.dmn.validation.DMNValidator.Validation.VALIDATE_SCHEMA;
@@ -608,6 +616,42 @@ class ValidatorTest extends AbstractValidatorTest {
}
}
+ @Test
+ void
validateValidModelsWithClashingInheritedImportAndInputNameSimpleData() {
+ String basePath =
"valid_models/DMNv1_6/imports/same_import_and_input_name_simple_data";
+ String dmnImporting = String.format("%s/ImportingModel.dmn", basePath);
+ String dmnImported = String.format("%s/ImportedModel.dmn", basePath);
+ String[] modelFiles = new String[]{dmnImporting, dmnImported};
+ Resource[] resources =
Arrays.stream(modelFiles).map(ClassPathResource::new).toArray(Resource[]::new);
+ List<DMNMessage> retrieved = validatorBuilder.theseModels(resources);
+ assertThat(retrieved).isNotNull().isEmpty();
+ }
+
+ @Test
+ void
validateValidModelsWithClashingInheritedImportAndInputNameComplexData() {
+ String basePath =
"valid_models/DMNv1_6/imports/same_import_and_input_name_complex_data";
+ String dmnImporting = String.format("%s/ImportingModel.dmn", basePath);
+ String dmnImported = String.format("%s/ImportedModel.dmn", basePath);
+ String[] modelFiles = new String[]{dmnImporting, dmnImported};
+ Resource[] resources =
Arrays.stream(modelFiles).map(ClassPathResource::new).toArray(Resource[]::new);
+ List<DMNMessage> retrieved = validatorBuilder.theseModels(resources);
+ assertThat(retrieved).isNotNull().isEmpty();
+ }
+
+ @Test
+ void validateInvalidModelsWithSameImportAndInputName() {
+ String basePath = "invalid_models/DMNv1_6";
+ String dmnImporting =
String.format("%s/InvalidModelImportInputDataNameClash.dmn", basePath);
+ String dmnImported = String.format("%s/ParentModel.dmn", basePath);
+ Resource importingResource = new ClassPathResource(dmnImporting);
+ Resource importedResource = new ClassPathResource(dmnImported);
+ List<DMNMessage> retrieved =
validatorBuilder.theseModels(importingResource, importedResource);
+ assertThat(retrieved).isNotNull()
+ .hasSize(3) // There are other two messages
+ .anyMatch(dmnMessage ->
dmnMessage.getLevel().equals(Message.Level.ERROR))
+ .anyMatch(dmnMessage -> dmnMessage.getText().contains("The
referenced name is not unique with its scope"));
+ }
+
@Test
void validateInvalidNamespaceModels() {
String modelFilePath =
"invalid_models/DMNv1_5/DMN-invalid-namespaces.dmn";
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]