Skip to content
Closed

CQL v5 #1713

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
db631e6
WIP
antvaset Mar 25, 2026
cac566d
Merge branch 'main' into engine-structured-types
antvaset Mar 25, 2026
d70dd7d
Merge branch 'main' into engine-structured-types
antvaset Mar 26, 2026
cf5de72
Type compatibility checking
antvaset Mar 26, 2026
817453e
Small changes
antvaset Mar 26, 2026
b6c5e16
Clean up tests
antvaset Mar 26, 2026
7adc520
detekt fix
antvaset Mar 26, 2026
f54c46f
Cleanup
antvaset Mar 26, 2026
fa15db8
Argument type check
antvaset Mar 26, 2026
cf1a572
KDocs
antvaset Mar 26, 2026
5686c6e
KDocs
antvaset Mar 26, 2026
e5eda12
Formatting
antvaset Mar 27, 2026
da737a2
Tests for `FhirModelResolver.toCqlValue()`
antvaset Mar 27, 2026
7b4a425
Add tests for `is`
antvaset Mar 27, 2026
e658833
Replace Java `Class<T>` with type QNames
antvaset Mar 27, 2026
51959b0
Docs
antvaset Mar 31, 2026
a44b03b
Docs
antvaset Mar 31, 2026
600be2f
Simplify engine environment class
antvaset Mar 31, 2026
cb7a3f5
Docs
antvaset Mar 31, 2026
4303518
Use safe cast to `Iterable<T>`
antvaset Apr 3, 2026
55cfb3f
Helper methods for `CqlClassInstance`
antvaset Apr 3, 2026
29e5de5
Merge branch 'main' into engine-structured-types
antvaset Apr 8, 2026
84fb401
Merge branch 'main' into engine-structured-types
antvaset Apr 14, 2026
3ebad16
Merge branch 'main' into engine-structured-types
antvaset Apr 15, 2026
4d2d655
Merge branch 'main' into engine-structured-types
antvaset Apr 16, 2026
2cc9d22
CQL value hierarchy in Kotlin classes in the engine
antvaset Apr 17, 2026
a2aaf78
Merge branch 'main' into engine-structured-types
antvaset Apr 26, 2026
162aee1
New sealed `Value` interface for CQL values (incl. new `Boolean`, `In…
antvaset Apr 28, 2026
8087d7f
Merge branch 'main' into engine-structured-types
antvaset Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import org.hl7.elm.r1.Expression
import org.hl7.elm.r1.Library
import org.opencds.cqf.cql.engine.execution.CqlEngine
import org.opencds.cqf.cql.engine.execution.Environment
import org.opencds.cqf.cql.engine.runtime.Value

object ElmEvaluationHelper {
@JvmStatic
fun evaluate(
library: Library?,
value: Expression?,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
): Any? {
): Value? {
// NOTE: Consider caching for libraries in the future.

val engine = getEngine(library, parameters, evaluationDateTime)
Expand All @@ -24,7 +25,7 @@ object ElmEvaluationHelper {

private fun getEngine(
library: Library?,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
): CqlEngine {
val environment = Environment(libraryManager)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,20 @@ import org.hl7.elm.r1.UsingDef
import org.hl7.elm.r1.ValueSetDef
import org.hl7.elm.r1.ValueSetRef
import org.hl7.elm.r1.VersionedIdentifier
import org.opencds.cqf.cql.engine.runtime.Value

class ElmRequirementsContext(
libraryManager: LibraryManager,
options: CqlCompilerOptions?,
visitor: ElmRequirementsVisitor,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
) {
var options: CqlCompilerOptions?

val libraryManager: LibraryManager

val parameters: MutableMap<String, Any?>?
val parameters: MutableMap<String, Value?>?

val evaluationDateTime: ZonedDateTime?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.cqframework.cql.elm.requirements.fhir
import ca.uhn.fhir.context.FhirVersionEnum
import java.time.ZonedDateTime
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.Any
import kotlin.Boolean
import kotlin.Exception
import kotlin.IllegalArgumentException
Expand Down Expand Up @@ -88,6 +87,7 @@ import org.hl7.fhir.r5.model.RelatedArtifact
import org.hl7.fhir.r5.model.StringType
import org.hl7.fhir.utilities.validation.ValidationMessage
import org.opencds.cqf.cql.engine.fhir.converter.FhirTypeConverterFactory
import org.opencds.cqf.cql.engine.runtime.Value

@Suppress("MaxLineLength", "ReturnCount", "ForbiddenComment", "NestedBlockDepth", "UnusedParameter")
class DataRequirementsProcessor {
Expand Down Expand Up @@ -127,7 +127,7 @@ class DataRequirementsProcessor {
translatedLibrary: CompiledLibrary,
options: CqlCompilerOptions,
expressions: Set<String>?,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
includeLogicDefinitions: Boolean,
recursive: Boolean,
): Library {
Expand Down Expand Up @@ -169,7 +169,7 @@ class DataRequirementsProcessor {
translatedLibrary: CompiledLibrary,
options: CqlCompilerOptions,
expressions: Set<String>?,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
includeLogicDefinitions: Boolean,
recursive: Boolean,
Expand Down Expand Up @@ -400,7 +400,7 @@ class DataRequirementsProcessor {
requirements: ElmRequirements,
libraryIdentifier: VersionedIdentifier,
expressionDefs: Iterable<ExpressionDef?>,
parameters: Map<String, Any?>?,
parameters: Map<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
includeLogicDefinitions: Boolean,
): Library {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.cqframework.cql.elm.requirements.fhir.utilities.constants

object CqfConstants {
const val DIRECT_REF_CODE_EXT_URL: String =
const val DIRECT_REF_CODE_EXT_URL =
"http://hl7.org/fhir/StructureDefinition/cqf-directReferenceCode"
const val LOGIC_DEFINITION_EXT_URL: String =
const val LOGIC_DEFINITION_EXT_URL =
"http://hl7.org/fhir/StructureDefinition/cqf-logicDefinition"
const val RELATED_REQUIREMENT_EXT_URL: String =
const val RELATED_REQUIREMENT_EXT_URL =
"http://hl7.org/fhir/StructureDefinition/cqf-relatedRequirement"
const val PERTINENCE_EXT_URL: String = "http://hl7.org/fhir/StructureDefinition/cqf-pertinence"
const val CQF_TOOLING_DEVICE_NAME: String = "cqf-tooling"
const val PERTINENCE_EXT_URL = "http://hl7.org/fhir/StructureDefinition/cqf-pertinence"
const val CQF_TOOLING_DEVICE_NAME = "cqf-tooling"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ package org.cqframework.cql.elm.requirements.fhir.utilities.constants
// constants defined in the Quality Measures IG: http://hl7.org/fhir/us/cqfmeasures
object CqfmConstants {
// Extensions
const val PARAMETERS_EXT_URL: String =
const val PARAMETERS_EXT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-parameter"
const val DATA_REQUIREMENT_EXT_URL: String =
const val DATA_REQUIREMENT_EXT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-dataRequirement"
const val RELATED_REQUIREMENT_EXT_URL: String =
const val RELATED_REQUIREMENT_EXT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-relatedRequirement"
const val DIRECT_REF_CODE_EXT_URL: String =
const val DIRECT_REF_CODE_EXT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode"
const val LOGIC_DEFINITION_EXT_URL: String =
const val LOGIC_DEFINITION_EXT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-logicDefinition"
const val PERTINENCE_EXT_URL: String =
const val PERTINENCE_EXT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-pertinence"
const val EFFECTIVE_DATA_REQS_EXT_URL: String =
const val EFFECTIVE_DATA_REQS_EXT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements"

// Profiles
const val COMPUTABLE_MEASURE_PROFILE_URL: String =
const val COMPUTABLE_MEASURE_PROFILE_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm"
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package org.cqframework.cql.elm.requirements.fhir.utilities.constants

object CrmiConstants {
const val EFFECTIVE_DATA_REQUIREMENTS_EXT_URL: String =
const val EFFECTIVE_DATA_REQUIREMENTS_EXT_URL =
"http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-effectiveDataRequirements"
const val EFFECTIVE_DATA_REQUIREMENTS_IDENTIFIER: String = "effective-data-requirements"
const val EFFECTIVE_DATA_REQUIREMENTS_IDENTIFIER = "effective-data-requirements"

const val SOFTWARE_SYSTEM_EXT_URL: String =
const val SOFTWARE_SYSTEM_EXT_URL =
"http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-softwaresystem"
const val SOFTWARE_SYSTEM_DEVICE_PROFILE_URL: String =
const val SOFTWARE_SYSTEM_DEVICE_PROFILE_URL =
"http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-softwaresystemdevice"
const val SOFTWARE_SYSTEM_DEVICE_TYPE_SYSTEM_URL: String =
const val SOFTWARE_SYSTEM_DEVICE_TYPE_SYSTEM_URL =
"http://terminology.hl7.org/CodeSystem/software-system-type"
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.opencds.cqf.cql.engine.runtime.Value
import org.slf4j.Logger
import org.slf4j.LoggerFactory

Expand Down Expand Up @@ -1069,7 +1070,7 @@ class DataRequirementsProcessorTest {
private fun getModuleDefinitionLibrary(
setup: Setup,
cqlTranslatorOptions: CqlCompilerOptions,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
): Library {
val dqReqTrans = DataRequirementsProcessor()
val moduleDefinitionLibrary =
Expand All @@ -1094,7 +1095,7 @@ class DataRequirementsProcessorTest {
private fun getModuleDefinitionLibrary(
setup: Setup,
cqlTranslatorOptions: CqlCompilerOptions,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
): Library {
val dqReqTrans = DataRequirementsProcessor()
Expand Down Expand Up @@ -1122,7 +1123,7 @@ class DataRequirementsProcessorTest {
private fun getModuleDefinitionLibrary(
setup: Setup,
cqlTranslatorOptions: CqlCompilerOptions,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
includeLogicDefinitions: Boolean,
): Library {
Expand Down Expand Up @@ -1151,7 +1152,7 @@ class DataRequirementsProcessorTest {
private fun getModuleDefinitionLibrary(
setup: Setup,
cqlTranslatorOptions: CqlCompilerOptions,
parameters: MutableMap<String, Any?>?,
parameters: MutableMap<String, Value?>?,
evaluationDateTime: ZonedDateTime?,
includeLogicDefinitions: Boolean,
recursive: Boolean,
Expand Down
16 changes: 16 additions & 0 deletions Src/java/engine-fhir/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# engine-fhir

This module contains the implementation of the FHIR model resolvers and retrieve providers for the CQL engine, based on the HAPI FHIR library.


## Converting from HAPI FHIR structures to engine value types

The `FhirModelResolver.toCqlValue()` helper method converts HAPI FHIR structures (resources and individual elements) to appropriate `ClassInstance`s, which can then be used as inputs to the engine. Given a FHIR structure, the method:

- walks all child elements of the FHIR structure (using introspection methods and reflections on HAPI FHIR classes) and adds an entry for every child in the named tuple being built,
- checks max cardinality to determine if a CQL list or a singleton to use as the value,
- if the child FHIR element has no values, sets the element value to `null` in the tuple.

In general, the least nested structure is chosen for the value of an element, e.g.:
- when the element is List-typed but has no values, `null` is chosen instead of an empty list
- when an element is FHIR primitive-typed (e.g. `FHIR.string` or `FHIR.code`-typed) and does not have value/id/extensions, `null` is chosen instead of a blank structure like `FHIR.string { id: null, extension: null, value: null }`
Loading
Loading