Skip to content

Commit

Permalink
[#655] DMN 1.5: Import with empty prefix. Change model names to match…
Browse files Browse the repository at this point in the history
… file names and the resolution of the root model for TCK tests (DMN15-139)
  • Loading branch information
opatrascoiu committed Feb 11, 2025
1 parent cc45215 commit ec6f969
Show file tree
Hide file tree
Showing 196 changed files with 341 additions and 319 deletions.
30 changes: 22 additions & 8 deletions dmn-core/src/main/java/com/gs/dmn/DMNModelRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class DMNModelRepository {
// Derived properties to optimise search
protected final List<TDefinitions> allDefinitions = new ArrayList<>();
protected final Map<String, TDefinitions> namespaceToDefinitions = new LinkedHashMap<>();
// Cache for DRGElements and top ItemDefinitions
protected final Map<TNamedElement, TDefinitions> elementToDefinitions = new LinkedHashMap<>();
protected List<TInvocable> invocables;
protected List<TItemDefinition> itemDefinitions;
Expand Down Expand Up @@ -71,6 +72,7 @@ public DMNModelRepository(List<TDefinitions> definitionsList) {
if (definitionsList != null) {
for (TDefinitions definitions: definitionsList) {
this.allDefinitions.add(definitions);
// Update caches
if (!this.namespaceToDefinitions.containsKey(definitions.getNamespace())) {
this.namespaceToDefinitions.put(definitions.getNamespace(), definitions);
} else {
Expand Down Expand Up @@ -182,14 +184,20 @@ public List<TDefinitions> getAllDefinitions() {
}

// Model name might not be unique
public List<TDefinitions> findDefinitionByName(String modelName) {
public TDefinitions findModelByName(String modelName) {
List<TDefinitions> result = new ArrayList<>();
for (TDefinitions definitions : this.allDefinitions) {
if (modelName.equals(definitions.getName())) {
result.add(definitions);
}
}
return result;
if (result.isEmpty()) {
throw new DMNRuntimeException(String.format("Cannot find model '%s'", modelName));
} else if (result.size() == 1) {
return result.get(0);
} else {
throw new DMNRuntimeException(String.format("Model name '%s' is not unique", modelName));
}
}

public void addElementMap(TDRGElement element, TDefinitions definitions) {
Expand All @@ -206,14 +214,20 @@ public List<String> getImportedNames() {
return names;
}

public TDefinitions getModel(String namespace) {
return this.namespaceToDefinitions.get(namespace);
}

public TDefinitions getModel(TNamedElement element) {
return this.elementToDefinitions.get(element);
}

public TDefinitions findModelByNamespace(String namespace) {
TDefinitions definitions = this.namespaceToDefinitions.get(namespace);
if (definitions == null) {
throw new DMNRuntimeException(String.format("Cannot find DM for namespace '%s'", namespace));
} else {
return definitions;
}
}

// Error location
public String makeLocation(TDefinitions definitions, TDMNElement element) {
if (definitions == null && element == null) {
return null;
Expand Down Expand Up @@ -612,7 +626,7 @@ public TDRGElement findDRGElementByName(String name) {
result = value.get(0);
this.drgElementByName.put(name, result);
} else if (value.size() > 1) {
throw new DMNRuntimeException(String.format("Found %s business knowledge models for name='%s'", value.size(), name));
throw new DMNRuntimeException(String.format("Found %s DRG elements for name='%s'", value.size(), name));
}
}
if (result == null) {
Expand Down Expand Up @@ -838,7 +852,7 @@ public TItemDefinition lookupItemDefinition(TDefinitions model, QualifiedName ty
for (TImport import_: model.getImport()) {
if (import_.getName().equals(importName)) {
String modelNamespace = import_.getNamespace();
model = this.getModel(modelNamespace);
model = this.findModelByNamespace(modelNamespace);
if (model == null) {
throw new DMNRuntimeException(String.format("Cannot find DM for '%s'", modelNamespace));
}
Expand Down
65 changes: 37 additions & 28 deletions dmn-core/src/main/java/com/gs/dmn/tck/TCKUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private DRGElementReference<? extends TDRGElement> extractInfoFromModel(TDefinit
// Lookup in imports
for (TImport imp: definitions.getImport()) {
String namespace = imp.getNamespace();
TDefinitions child = this.dmnModelRepository.getModel(namespace);
TDefinitions child = this.dmnModelRepository.findModelByNamespace(namespace);
DRGElementReference<? extends TDRGElement> result = extractInfoFromModel(child, elementNamespace, elementName, new ImportPath(importPath, imp.getName()));
if (result != null) {
return result;
Expand All @@ -124,7 +124,7 @@ private DRGElementReference<? extends TDRGElement> extractInfoFromModel(TDefinit
// Lookup in imports
for (TImport imp: definitions.getImport()) {
String namespace = imp.getNamespace();
TDefinitions child = this.dmnModelRepository.getModel(namespace);
TDefinitions child = this.dmnModelRepository.findModelByNamespace(namespace);
DRGElementReference<? extends TDRGElement> result = extractInfoFromModel(child, elementName, new ImportPath(importPath, imp.getName()));
if (result != null) {
return result;
Expand All @@ -141,7 +141,7 @@ public Pair<DRGElementReference<? extends TDRGElement>, ValueType> extractInfoFr
ValueType value = node;
while (import_ != null) {
path.addPathElement(name);
definitions = this.dmnModelRepository.getModel(import_.getNamespace());
definitions = this.dmnModelRepository.findModelByNamespace(import_.getNamespace());
name = null;
if (value.getComponent() != null && value.getComponent().size() == 1) {
Component component = value.getComponent().get(0);
Expand Down Expand Up @@ -508,37 +508,32 @@ private List<Object> makeArgs(TDRGElement drgElement, TestCase testCase) {
// Model - lookup methods
//
private TDefinitions getRootModel(TestCases testCases) {
TDefinitions definitions;
if (this.dmnModelRepository.getAllDefinitions().size() == 1) {
// One single DM
definitions = this.dmnModelRepository.getRootDefinitions();
// Find DM by namespace or name
String namespace = getNamespace(testCases);
TDefinitions definitions = null;
if (StringUtils.isBlank(namespace)) {
// Lookup by name
String modelName = getModelName(testCases);
definitions = this.dmnModelRepository.findModelByName(modelName);
} else {
// Find DM by namespace
String namespace = getNamespace(testCases);
if (!StringUtils.isEmpty(namespace)) {
definitions = this.dmnModelRepository.getModel(namespace);
} else {
throw new DMNRuntimeException(String.format("Missing namespace for TestCases '%s'", testCases.getModelName()));
}
// Lookup by namespace
definitions = this.dmnModelRepository.findModelByNamespace(namespace);
}
if (definitions == null) {
throw new DMNRuntimeException(String.format("Cannot find root DM for TestCases '%s'", testCases.getModelName()));
throw new DMNRuntimeException(String.format("Cannot find DM '%s' for TestCases", testCases.getModelName()));
} else {
return definitions;
}
}

private TDRGElement findDRGElement(TestCases testCases, TestCase testCase, ResultNode node) {
try {
String namespace = getNamespace(testCases, testCase, node);
String name = drgElementName(testCases, testCase, node);
if (namespace != null) {
return this.dmnModelRepository.findDRGElementByName(namespace, name);
} else {
return this.dmnModelRepository.findDRGElementByName(name);
}
} catch (Exception e) {
return null;
String namespace = getNamespace(testCases, testCase, node);
String name = drgElementName(testCases, testCase, node);
if (!StringUtils.isBlank(namespace)) {
return this.dmnModelRepository.findDRGElementByName(namespace, name);
} else {
TDefinitions rootModel = getRootModel(testCases);
return this.dmnModelRepository.findDRGElementByName(rootModel, name);
}
}

Expand All @@ -556,6 +551,17 @@ private String drgElementName(TestCases testCases, TestCase testCase, ResultNode
return elementToEvaluate;
}

private String getModelName(TestCases testCases) {
String fileName = testCases.getModelName();
return getModelName(fileName);
}

public static String getModelName(String fileName) {
// Remove extension
int index = fileName.indexOf(".");
return index == -1 ? fileName : fileName.substring(0, index);
}

private String getNamespace(TestCases testCases) {
return testCases.getNamespace();
}
Expand All @@ -566,18 +572,21 @@ private String getNamespace(TestCase testCase) {

private String getNamespace(TestCases testCases, TestCase testCase, InputNode node) {
String namespace = getNamespace(node);
if (StringUtils.isEmpty(namespace)) {
if (StringUtils.isBlank(namespace)) {
namespace = getNamespace(testCase);
}
if (StringUtils.isBlank(namespace)) {
namespace = getNamespace(testCases);
}
return namespace;
}

private String getNamespace(TestCases testCases, TestCase testCase, ResultNode node) {
String namespace = getNamespace(node);
if (StringUtils.isEmpty(namespace)) {
if (StringUtils.isBlank(namespace)) {
namespace = getNamespace(testCase);
}
if (StringUtils.isEmpty(namespace)) {
if (StringUtils.isBlank(namespace)) {
namespace = getNamespace(testCases);
}
return namespace;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.gs.dmn.log.BuildLogger;
import com.gs.dmn.log.Slf4jBuildLogger;
import com.gs.dmn.runtime.Pair;
import com.gs.dmn.tck.TCKUtil;
import com.gs.dmn.tck.ast.*;
import com.gs.dmn.tck.ast.visitor.TraversalVisitor;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -76,13 +77,11 @@ public AddMissingImportPrefixInDTVisitor(ErrorHandler errorHandler) {
public TCKBaseElement visit(TestCases element, TransformationContext context) {
DMNModelRepository repository = context.getRepository();
if (element != null) {
// Search model by name
// Set TestCase namespace from model under test
if (StringUtils.isBlank(element.getNamespace())) {
String modelName = element.getModelName();
List<TDefinitions> definitionsList = repository.findDefinitionByName(modelName);
if (definitionsList.size() == 1) {
element.setNamespace(definitionsList.get(0).getNamespace());
}
String modelName = TCKUtil.getModelName(element.getModelName());
TDefinitions definitions = repository.findModelByName(modelName);
element.setNamespace(definitions.getNamespace());
}

// Set namespace for nodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private String findNamespace(TDefinitions model, String importName) {
}
for (TImport import_: model.getImport()) {
if (import_.getName().equals(importName)) {
model = this.dmnModelRepository.getModel(import_.getNamespace());
model = this.dmnModelRepository.findModelByNamespace(import_.getNamespace());
if (model == null) {
throw new DMNRuntimeException(String.format("Cannot find model for import name '%s'", importName));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public void testValidateWhenCorrect() {
@Test
public void testValidateDefinitionsWhenNotUniqueNames() {
List<String> expectedErrors = Arrays.asList(
"(model='definitions'): error: The 'name' of a 'DRGElement' must be unique. Found duplicates for 'CIP Assessments, Input'.",
"(model='definitions'): error: The 'name' of a 'ItemDefinition' must be unique. Found duplicates for 'itemDefinition'.",
"(model='definitions', name='CIP Assessments', id='cip-assessments'): error: Missing variable",
"(model='definitions', name='CIP Assessments', id='cip-assessments1'): error: Missing variable",
"(model='definitions', label='String', name='CIP Assessments', id='id-input-1'): error: DRGElement name and variable name should be the same. Found 'CIP Assessments' and 'string'"
"(model='test-dmn-with-duplicates'): error: The 'name' of a 'DRGElement' must be unique. Found duplicates for 'CIP Assessments, Input'.",
"(model='test-dmn-with-duplicates'): error: The 'name' of a 'ItemDefinition' must be unique. Found duplicates for 'itemDefinition'.",
"(model='test-dmn-with-duplicates', name='CIP Assessments', id='cip-assessments'): error: Missing variable",
"(model='test-dmn-with-duplicates', name='CIP Assessments', id='cip-assessments1'): error: Missing variable",
"(model='test-dmn-with-duplicates', label='String', name='CIP Assessments', id='id-input-1'): error: DRGElement name and variable name should be the same. Found 'CIP Assessments' and 'string'"
);
validate(validator, resource("dmn/input/1.1/test-dmn-with-duplicates.dmn"), expectedErrors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,41 +41,41 @@ public void testValidateWhenRepositoryIsEmpty() {
@Test
public void testValidateWhenIntervals1() {
List<String> expectedErrors = Arrays.asList(
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1500, 2000)' is not covered for column 1 in 'Loan Grade' table",
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(3000, 4000)' is not covered for column 2 in 'Loan Grade' table"
"(model='loan-grade-with-intervals-1', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1500, 2000)' is not covered for column 1 in 'Loan Grade' table",
"(model='loan-grade-with-intervals-1', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(3000, 4000)' is not covered for column 2 in 'Loan Grade' table"
);
validate(validator, resource("dmn/input/1.3/loan-grade-with-intervals-1.dmn"), expectedErrors);
}

@Test
public void testValidateWhenIntervals2() {
List<String> expectedErrors = Arrays.asList(
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1500, 1600)' is not covered for column 1 in 'Loan Grade' table",
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1000, 1200)' is not covered for column 2 in 'Loan Grade' table"
"(model='loan-grade-with-intervals-2', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1500, 1600)' is not covered for column 1 in 'Loan Grade' table",
"(model='loan-grade-with-intervals-2', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1000, 1200)' is not covered for column 2 in 'Loan Grade' table"
);
validate(validator, resource("dmn/input/1.3/loan-grade-with-intervals-2.dmn"), expectedErrors);
}

@Test
public void testValidateWhenIntervals3() {
List<String> expectedErrors = Collections.singletonList(
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1500, 1600)' is not covered for column 1 in 'Loan Grade' table"
"(model='loan-grade-with-intervals-3', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(1500, 1600)' is not covered for column 1 in 'Loan Grade' table"
);
validate(validator, resource("dmn/input/1.3/loan-grade-with-intervals-3.dmn"), expectedErrors);
}

@Test
public void testValidateWhenRelationalOperators() {
List<String> expectedErrors = Collections.singletonList(
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(3000, 4000)' is not covered for column 2 in 'Loan Grade' table"
"(model='loan-grade-with-relational-operators', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(3000, 4000)' is not covered for column 2 in 'Loan Grade' table"
);
validate(validator, resource("dmn/input/1.3/loan-grade-with-relational-operators.dmn"), expectedErrors);
}

@Test
public void testValidateWhenAny() {
List<String> expectedErrors = Collections.singletonList(
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(3000, 4000)' is not covered for column 2 in 'Loan Grade' table"
"(model='loan-grade-with-any', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '(3000, 4000)' is not covered for column 2 in 'Loan Grade' table"
);
validate(validator, resource("dmn/input/1.3/loan-grade-with-any.dmn"), expectedErrors);
}
Expand All @@ -95,7 +95,7 @@ public void testValidateWhenEnumeration() {
@Test
public void testValidateWhenEnumerationMissing() {
List<String> expectedErrors = Collections.singletonList(
"(model='loan-grade', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '{\"E23\"}' is not covered for column 2 in 'Loan Grade' table"
"(model='loan-grade-with-enumeration-missing', name='Loan Grade', id='_FAF682B2-D00A-469A-8B7D-932154DA95E0'): error: Interval '{\"E23\"}' is not covered for column 2 in 'Loan Grade' table"
);
validate(validator, resource("dmn/input/1.3/loan-grade-with-enumeration-missing.dmn"), expectedErrors);
}
Expand Down
Loading

0 comments on commit ec6f969

Please sign in to comment.