diff --git a/pom.xml b/pom.xml
index 6b2166f..055e6e5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
gov.cms.madie
madie-translator-commons
- 2.0.2-SNAPSHOT
+ 2.0.3-SNAPSHOT
UTF-8
17
diff --git a/src/main/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLTools.java b/src/main/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLTools.java
index 8da0f57..f7f956d 100644
--- a/src/main/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLTools.java
+++ b/src/main/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLTools.java
@@ -398,12 +398,13 @@ private void collectReturnTypeMap() {
this.allNamesToReturnTypeMap.put(libraryName + "-" + libraryVersion, new HashMap<>());
for (ExpressionDef expression : statements.getDef()) {
+ String resultType =
+ expression.getResultType() == null ? null : expression.getResultType().toString();
this.allNamesToReturnTypeMap
.get(libraryName + "-" + libraryVersion)
- .put(expression.getName(), expression.getResultType().toString());
- this.nameToReturnTypeMap.put(expression.getName(), expression.getResultType().toString());
- this.expressionToReturnTypeMap.put(
- expression.getName(), expression.getResultType().toString());
+ .put(expression.getName(), resultType);
+ this.nameToReturnTypeMap.put(expression.getName(), resultType);
+ this.expressionToReturnTypeMap.put(expression.getName(), resultType);
}
if (parameters != null) {
diff --git a/src/test/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLToolsTest.java b/src/test/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLToolsTest.java
index b337490..e93d9d0 100644
--- a/src/test/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLToolsTest.java
+++ b/src/test/java/gov/cms/madie/cql_elm_translator/utils/cql/CQLToolsTest.java
@@ -1,18 +1,29 @@
package gov.cms.madie.cql_elm_translator.utils.cql;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import lombok.extern.slf4j.Slf4j;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.ResolvedIdentifierContext;
+import org.hl7.cql.model.DataType;
+import org.hl7.elm.r1.IncludeDef;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@@ -26,6 +37,7 @@
import org.hl7.elm.r1.ExpressionDef;
@ExtendWith(MockitoExtension.class)
+@Slf4j
class CQLToolsTest {
@Mock private CqlTranslator cqlTranslator;
@@ -33,6 +45,8 @@ class CQLToolsTest {
@Mock private Library library;
@Mock private ResolvedIdentifierContext element;
@Mock private Statements statements;
+ @Mock private Library.Includes includes;
+ @Mock private Map compiledLibraryMap;
VersionedIdentifier versionedIdentifier = new VersionedIdentifier();
@@ -59,4 +73,66 @@ void testGenerate() {
fail();
}
}
+
+ @Test
+ void testItHandlesNullExpressionResultType() {
+ versionedIdentifier.setId("local");
+ String cqlData = ResourceUtils.getData("/tooling_test_qicore6.cql");
+ doReturn(statements).when(library).getStatements();
+ doReturn(compiledLibrary).when(cqlTranslator).getTranslatedLibrary();
+ doReturn(library).when(compiledLibrary).getLibrary();
+ doReturn(versionedIdentifier).when(compiledLibrary).getIdentifier();
+ doReturn(element).when(compiledLibrary).resolve(any(String.class));
+ doReturn(includes).when(library).getIncludes();
+
+ ExpressionDef expression1 = mock(ExpressionDef.class);
+ DataType booleanDataType = mock(DataType.class);
+ doReturn(booleanDataType).when(expression1).getResultType();
+ doReturn("expression1").when(expression1).getName();
+ doReturn("System.Boolean").when(booleanDataType).toString();
+
+ ExpressionDef expression2 = mock(ExpressionDef.class);
+ doReturn("expression2").when(expression2).getName();
+ doReturn(null).when(expression2).getResultType();
+
+ List expressionDefs = List.of(expression1, expression2);
+
+ doReturn(expressionDefs).when(statements).getDef();
+
+ Set parentExpressions = new HashSet<>();
+ IncludeDef includeDef1 = mock(IncludeDef.class);
+ List includeDefs = List.of(includeDef1);
+ doReturn(includeDefs).when(includes).getDef();
+ doReturn("SupplementalDataElements").when(includeDef1).getPath();
+ CompiledLibrary sdeCompiledLib = mock(CompiledLibrary.class);
+ Library sdeLib = mock(Library.class);
+ doReturn(sdeLib).when(sdeCompiledLib).getLibrary();
+ VersionedIdentifier sdeLibVersionedIdentifier = new VersionedIdentifier();
+ sdeLibVersionedIdentifier.setId("SupplementalDataElements");
+ doReturn(sdeLibVersionedIdentifier).when(sdeCompiledLib).getIdentifier();
+
+ Library.Statements statements1 = mock(Library.Statements.class);
+ doReturn(statements1).when(sdeLib).getStatements();
+ Library.Parameters parameters1 = mock(Library.Parameters.class);
+ doReturn(parameters1).when(sdeLib).getParameters();
+
+ doReturn(sdeCompiledLib).when(compiledLibraryMap).get(anyString());
+
+ CQLTools cqlTools =
+ new CQLTools(cqlData, null, parentExpressions, cqlTranslator, compiledLibraryMap);
+ assertNotNull(cqlTools);
+ try {
+ cqlTools.generate();
+ Set contents = cqlTools.getDefinitionContents();
+ assertNotNull(contents);
+ Map expressionToReturnTypeMap = cqlTools.getExpressionToReturnTypeMap();
+ assertThat(expressionToReturnTypeMap, is(notNullValue()));
+ assertThat(expressionToReturnTypeMap.containsKey("expression1"), is(true));
+ assertThat(expressionToReturnTypeMap.containsKey("expression2"), is(true));
+ assertThat(expressionToReturnTypeMap.get("expression1"), is(equalTo("System.Boolean")));
+ assertThat(expressionToReturnTypeMap.get("expression2"), is(nullValue()));
+ } catch (IOException e) {
+ fail();
+ }
+ }
}
diff --git a/src/test/resources/tooling_test_qicore6.cql b/src/test/resources/tooling_test_qicore6.cql
new file mode 100644
index 0000000..e2aaf9a
--- /dev/null
+++ b/src/test/resources/tooling_test_qicore6.cql
@@ -0,0 +1,61 @@
+library QM61 version '0.0.000'
+
+using QICore version '6.0.0'
+
+include QICoreCommon version '3.0.000' called QICoreCommon
+include FHIRHelpers version '4.4.000' called FHIRHelpers
+include SupplementalDataElements version '4.3.000' called SDE
+include Hospice version '7.0.000' called Hospice
+include Status version '2.0.000' called Status
+
+codesystem "LOINC": 'http://loinc.org'
+codesystem "SNOMEDCT": 'http://snomed.info/sct'
+
+valueset "Clinical Oral Evaluation": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1003'
+valueset "Dental Caries": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1004'
+valueset "Discharged to Health Care Facility for Hospice Care": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.207'
+valueset "Discharged to Home for Hospice Care": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.209'
+valueset "Encounter Inpatient": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307'
+
+code "Discharge to healthcare facility for hospice care (procedure)": '428371000124100' from "SNOMEDCT" display 'Discharge to healthcare facility for hospice care (procedure)'
+code "Discharge to home for hospice care (procedure)": '428361000124107' from "SNOMEDCT" display 'Discharge to home for hospice care (procedure)'
+code "Hospice care [Minimum Data Set]": '45755-6' from "LOINC" display 'Hospice care [Minimum Data Set]'
+code "Yes (qualifier value)": '373066001' from "SNOMEDCT" display 'Yes (qualifier value)'
+
+parameter "Measurement Period" Interval
+
+context Patient
+
+
+define "Initial Population":
+ AgeInYearsAt(date from start of "Measurement Period")in Interval[1, 20]
+ and exists ( "Qualifying Encounters" )
+
+define "Qualifying Encounters":
+ (([Encounter: "Clinical Oral Evaluation"]).isEncounterPerformed()) ValidEncounter
+ where ValidEncounter.period.toInterval() during day of "Measurement Period"
+
+define "Denominator":
+ "Initial Population"
+
+define "Denominator Exclusions":
+ Hospice."Has Hospice Services"
+
+define "Numerator":
+exists ["QICore Condition Problems Health Concerns": "Dental Caries"] DentalCaries
+ where DentalCaries.prevalenceInterval() overlaps "Measurement Period"
+
+define "SDE Ethnicity":
+ SDE."SDE Ethnicity"
+
+define "SDE Payer":
+ SDE."SDE Payer"
+
+define "SDE Race":
+ SDE."SDE Race"
+
+define "SDE Sex":
+ SDE."SDE Sex"
+
+define "SDE Eth":
+ SDE."SDE Eth"