From fe9fb6b461687a0c79a4b1e7f44580618fe02c6f Mon Sep 17 00:00:00 2001 From: Bryn Rhodes Date: Wed, 18 Jan 2023 09:57:41 -0700 Subject: [PATCH] Feature 850 datetime datarequirements (#1111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #850: Adding optimization of datetime filters to data requirements analysis. * release -v2.4.0 (#844) * Snapshot v2.5.0 (#846) * release -v2.4.0 * snapshot -v2.5.0 * Merging the engine repository into this repository to reduce overall … (#854) Merging the engine repository into this repository to reduce overall number of repositories in the CQF stack * Create add-to-platform-project.yml (#1105) * #850: Enhanced data requirements analysis/gather with compile-time evaluation of date filter comparand targets. Also fixed parameter resolution issues, FHIR-CQL quantity conversion issues, and added support for parameterization and asOf behavior in data requirements gather. * Additional test cases for parameterized data requirements analysis Corrected data requirements inference generally for operator invocations Added complete ELM comparison capability to the SimpleElmEngine Fixed FHIR type conversions for quantity and datetime values Changed DateTime MinValue and MaxValue to return UTC timezoneoffset values Fixed ToDateTime not respecting evaluationDateTime timezoneoffset Co-authored-by: mdnazmulkarim Co-authored-by: JP --- Src/java/elm-fhir/build.gradle | 16 + .../cql/elm/ExcludeFromMapping.java | 15 + .../cqframework/cql/elm/LibraryMapper.java | 2169 +++++++++++++++++ .../cql/elm/evaluation/ElmAnalysisHelper.java | 301 +++ .../elm/evaluation/ElmEvaluationHelper.java | 31 + .../ElmConjunctiveRequirement.java | 11 +- .../ElmDisjunctiveRequirement.java | 13 +- .../ElmExpressionRequirement.java | 2 +- .../requirements/ElmOperatorRequirement.java | 9 +- .../requirements/ElmRequirementsContext.java | 15 +- .../requirements/ElmRequirementsVisitor.java | 17 +- .../fhir/DataRequirementsProcessor.java | 328 +-- .../cqframework/cql/elm/MapperCodeGen.java | 167 ++ .../fhir/DataRequirementsProcessorTest.java | 268 +- .../requirements/fhir/DataRequirements.cql | 121 +- .../fhir/TestCases/TestCase2e.cql | 25 + .../fhir/TestCases/TestCase2g.cql | 20 + .../fhir/TestCases/TestCase2i.cql | 18 + .../fhir/TestCases/TestCase2j.cql | 20 + .../Library-BSElements-data-requirements.json | 77 +- .../cql/elm/evaluating/SimpleElmEngine.java | 1323 +++++++++- .../fhir/converter/BaseFhirTypeConverter.java | 88 + .../converter/Dstu2FhirTypeConverter.java | 21 +- .../converter/Dstu3FhirTypeConverter.java | 21 +- .../fhir/converter/FhirTypeConverter.java | 24 + .../fhir/converter/R4FhirTypeConverter.java | 21 +- .../fhir/converter/R5FhirTypeConverter.java | 26 +- .../converter/Dstu2TypeConverterTests.java | 6 +- .../converter/Dstu3TypeConverterTests.java | 14 +- .../fhir/converter/R4TypeConverterTests.java | 14 +- .../fhir/converter/R5TypeConverterTests.java | 14 +- .../cql/CqlArithmeticFunctionsTest.xml | 4 +- .../execution/CqlArithmeticFunctionsTest.java | 4 +- .../execution/portable/CqlTestSuite.cql | 6 +- .../elm/execution/MaxValueEvaluator.java | 4 +- .../elm/execution/MinValueEvaluator.java | 4 +- .../elm/execution/ToDateTimeEvaluator.java | 7 +- .../cqf/cql/engine/execution/Context.java | 8 + .../cqf/cql/engine/execution/CqlEngine.java | 15 +- 39 files changed, 4772 insertions(+), 495 deletions(-) create mode 100644 Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/ExcludeFromMapping.java create mode 100644 Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/LibraryMapper.java create mode 100644 Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmAnalysisHelper.java create mode 100644 Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmEvaluationHelper.java create mode 100644 Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/MapperCodeGen.java create mode 100644 Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2e.cql create mode 100644 Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2g.cql create mode 100644 Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2i.cql create mode 100644 Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2j.cql diff --git a/Src/java/elm-fhir/build.gradle b/Src/java/elm-fhir/build.gradle index b79d7cc2e..8fd4a4352 100644 --- a/Src/java/elm-fhir/build.gradle +++ b/Src/java/elm-fhir/build.gradle @@ -1,9 +1,25 @@ // Most build configuration comes from the cql-all parent build! +ext { + mapstructVersion = "1.4.2.Final" +} + dependencies { implementation project(':cql-to-elm') + implementation project(':engine') + implementation project(":engine-fhir") + implementation "org.mapstruct:mapstruct:${mapstructVersion}" testImplementation project(':quick') + testImplementation "org.reflections:reflections:0.10.2" testRuntimeOnly project(':model-jackson') testRuntimeOnly group: 'xpp3', name: 'xpp3', version: '1.1.4c' + + annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" +} + +tasks.withType(JavaCompile) { + options.compilerArgs = [ + "-Amapstruct.suppressGeneratorTimestamp=true" + ] } diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/ExcludeFromMapping.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/ExcludeFromMapping.java new file mode 100644 index 000000000..c75750bbc --- /dev/null +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/ExcludeFromMapping.java @@ -0,0 +1,15 @@ +package org.cqframework.cql.elm; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.mapstruct.Qualifier; + +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; + +@Qualifier +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface ExcludeFromMapping { +} diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/LibraryMapper.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/LibraryMapper.java new file mode 100644 index 000000000..448d01bdd --- /dev/null +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/LibraryMapper.java @@ -0,0 +1,2169 @@ +package org.cqframework.cql.elm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface LibraryMapper { + + public static final LibraryMapper INSTANCE = Mappers.getMapper(LibraryMapper.class); + + default org.cqframework.cql.elm.execution.ExpressionDef map(org.hl7.elm.r1.ExpressionDef element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.FunctionDef) { + return map((org.hl7.elm.r1.FunctionDef) element); + } else { + return mapToEvaluator(element); + } + } + + @ExcludeFromMapping + org.opencds.cqf.cql.engine.elm.execution.ExpressionDefEvaluator mapToEvaluator( + org.hl7.elm.r1.ExpressionDef element); + + @Named("ExpressionToCodeSystemRef") + default org.cqframework.cql.elm.execution.CodeSystemRef mapCodeSystemRef(org.hl7.elm.r1.Expression expression) { + if (expression == null) { + return null; + } + + if (expression instanceof org.hl7.elm.r1.CodeSystemRef) { + return map((org.hl7.elm.r1.CodeSystemRef) expression); + } + + throw new IllegalArgumentException( + "unable to map type: " + expression.getClass().getName() + " to CodeSystemRef"); + } + + @Named("ExpressionToValueSetRef") + default org.cqframework.cql.elm.execution.ValueSetRef mapValueSetRef(org.hl7.elm.r1.Expression expression) { + if (expression == null) { + return null; + } + + if (expression instanceof org.hl7.elm.r1.ValueSetRef) { + return map((org.hl7.elm.r1.ValueSetRef) expression); + } + + throw new IllegalArgumentException( + "unable to map type: " + expression.getClass().getName() + " to ValueSetRef"); + } + + @Mapping(target = "annotation") + default List map( + List annotations) { + if (annotations == null) { + return null; + } + + if (annotations.isEmpty()) { + return Collections.emptyList(); + } + + List engineAnnotations = new ArrayList<>(); + for (org.hl7.cql_annotations.r1.CqlToElmBase a : annotations) { + if (a instanceof org.hl7.cql_annotations.r1.CqlToElmError) { + engineAnnotations.add(map((org.hl7.cql_annotations.r1.CqlToElmError) a)); + } else if (a instanceof org.hl7.cql_annotations.r1.CqlToElmInfo) { + engineAnnotations.add(map((org.hl7.cql_annotations.r1.CqlToElmInfo) a)); + } else if (a instanceof org.hl7.cql_annotations.r1.Locator) { + engineAnnotations.add(map((org.hl7.cql_annotations.r1.Locator) a)); + } else if (a instanceof org.hl7.cql_annotations.r1.Annotation) { + engineAnnotations.add(map((org.hl7.cql_annotations.r1.Annotation) a)); + } else { + throw new IllegalArgumentException( + String.format("Tried to map unknown Annotation type: %s", a.getClass().getSimpleName())); + } + } + + return engineAnnotations; + } + + org.cqframework.cql.elm.execution.CqlToElmError map(org.hl7.cql_annotations.r1.CqlToElmError element); + + org.cqframework.cql.elm.execution.CqlToElmInfo map(org.hl7.cql_annotations.r1.CqlToElmInfo element); + + org.cqframework.cql.elm.execution.Locator map(org.hl7.cql_annotations.r1.Locator element); + + org.cqframework.cql.elm.execution.Annotation map(org.hl7.cql_annotations.r1.Annotation element); + + @Mapping(target = "valueset", qualifiedByName = { "ExpressionToValueSetRef" }) + org.opencds.cqf.cql.engine.elm.execution.InValueSetEvaluator map(org.hl7.elm.r1.InValueSet element); + + @Mapping(target = "valueset", qualifiedByName = { "ExpressionToValueSetRef" }) + org.opencds.cqf.cql.engine.elm.execution.AnyInValueSetEvaluator map(org.hl7.elm.r1.AnyInValueSet element); + + @Mapping(target = "codesystem", qualifiedByName = { "ExpressionToCodeSystemRef" }) + org.opencds.cqf.cql.engine.elm.execution.InCodeSystemEvaluator map(org.hl7.elm.r1.InCodeSystem element); + + @Mapping(target = "codesystem", qualifiedByName = { "ExpressionToCodeSystemRef" }) + org.opencds.cqf.cql.engine.elm.execution.AnyInCodeSystemEvaluator map(org.hl7.elm.r1.AnyInCodeSystem element); + + org.opencds.cqf.cql.engine.elm.execution.NullEvaluator map(org.hl7.elm.r1.Null element); + + // Start of pre-code gen (See the MapperCodeGen class) + org.opencds.cqf.cql.engine.elm.execution.ToDateEvaluator map(org.hl7.elm.r1.ToDate element); + + org.opencds.cqf.cql.engine.elm.execution.QueryEvaluator map(org.hl7.elm.r1.Query element); + + org.opencds.cqf.cql.engine.elm.execution.SubstringEvaluator map(org.hl7.elm.r1.Substring element); + + org.opencds.cqf.cql.engine.elm.execution.ValueSetRefEvaluator map(org.hl7.elm.r1.ValueSetRef element); + + org.opencds.cqf.cql.engine.elm.execution.ProperContainsEvaluator map(org.hl7.elm.r1.ProperContains element); + + org.opencds.cqf.cql.engine.elm.execution.XorEvaluator map(org.hl7.elm.r1.Xor element); + + org.cqframework.cql.elm.execution.OperandDef map(org.hl7.elm.r1.OperandDef element); + + org.cqframework.cql.elm.execution.With map(org.hl7.elm.r1.With element); + + org.cqframework.cql.elm.execution.ByExpression map(org.hl7.elm.r1.ByExpression element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToStringEvaluator map(org.hl7.elm.r1.ConvertsToString element); + + org.cqframework.cql.elm.execution.CanConvert map(org.hl7.elm.r1.CanConvert element); + + org.cqframework.cql.elm.execution.IncludeDef map(org.hl7.elm.r1.IncludeDef element); + + org.opencds.cqf.cql.engine.elm.execution.ToDecimalEvaluator map(org.hl7.elm.r1.ToDecimal element); + + org.opencds.cqf.cql.engine.elm.execution.ToQuantityEvaluator map(org.hl7.elm.r1.ToQuantity element); + + org.opencds.cqf.cql.engine.elm.execution.TimeEvaluator map(org.hl7.elm.r1.Time element); + + org.opencds.cqf.cql.engine.elm.execution.BeforeEvaluator map(org.hl7.elm.r1.Before element); + + org.opencds.cqf.cql.engine.elm.execution.LessOrEqualEvaluator map(org.hl7.elm.r1.LessOrEqual element); + + org.opencds.cqf.cql.engine.elm.execution.ProductEvaluator map(org.hl7.elm.r1.Product element); + + org.opencds.cqf.cql.engine.elm.execution.PropertyEvaluator map(org.hl7.elm.r1.Property element); + + org.opencds.cqf.cql.engine.elm.execution.IndexOfEvaluator map(org.hl7.elm.r1.IndexOf element); + + org.opencds.cqf.cql.engine.elm.execution.ContainsEvaluator map(org.hl7.elm.r1.Contains element); + + org.opencds.cqf.cql.engine.elm.execution.IndexerEvaluator map(org.hl7.elm.r1.Indexer element); + + org.opencds.cqf.cql.engine.elm.execution.QuantityEvaluator map(org.hl7.elm.r1.Quantity element); + + org.opencds.cqf.cql.engine.elm.execution.CeilingEvaluator map(org.hl7.elm.r1.Ceiling element); + + org.opencds.cqf.cql.engine.elm.execution.EqualEvaluator map(org.hl7.elm.r1.Equal element); + + org.opencds.cqf.cql.engine.elm.execution.RetrieveEvaluator map(org.hl7.elm.r1.Retrieve element); + + org.opencds.cqf.cql.engine.elm.execution.TupleEvaluator map(org.hl7.elm.r1.Tuple element); + + org.opencds.cqf.cql.engine.elm.execution.CollapseEvaluator map(org.hl7.elm.r1.Collapse element); + + org.opencds.cqf.cql.engine.elm.execution.MeetsEvaluator map(org.hl7.elm.r1.Meets element); + + org.opencds.cqf.cql.engine.elm.execution.AddEvaluator map(org.hl7.elm.r1.Add element); + + org.opencds.cqf.cql.engine.elm.execution.ToStringEvaluator map(org.hl7.elm.r1.ToString element); + + org.cqframework.cql.elm.execution.TupleElementDefinition map(org.hl7.elm.r1.TupleElementDefinition element); + + org.opencds.cqf.cql.engine.elm.execution.PowerEvaluator map(org.hl7.elm.r1.Power element); + + org.opencds.cqf.cql.engine.elm.execution.LastEvaluator map(org.hl7.elm.r1.Last element); + + org.opencds.cqf.cql.engine.elm.execution.PointFromEvaluator map(org.hl7.elm.r1.PointFrom element); + + org.cqframework.cql.elm.execution.Subsumes map(org.hl7.elm.r1.Subsumes element); + + org.opencds.cqf.cql.engine.elm.execution.VarianceEvaluator map(org.hl7.elm.r1.Variance element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToDecimalEvaluator map(org.hl7.elm.r1.ConvertsToDecimal element); + + org.opencds.cqf.cql.engine.elm.execution.FunctionRefEvaluator map(org.hl7.elm.r1.FunctionRef element); + + org.opencds.cqf.cql.engine.elm.execution.ProperIncludesEvaluator map(org.hl7.elm.r1.ProperIncludes element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToDateEvaluator map(org.hl7.elm.r1.ConvertsToDate element); + + org.opencds.cqf.cql.engine.elm.execution.CombineEvaluator map(org.hl7.elm.r1.Combine element); + + org.opencds.cqf.cql.engine.elm.execution.EquivalentEvaluator map(org.hl7.elm.r1.Equivalent element); + + org.opencds.cqf.cql.engine.elm.execution.DivideEvaluator map(org.hl7.elm.r1.Divide element); + + org.opencds.cqf.cql.engine.elm.execution.ReplaceMatchesEvaluator map(org.hl7.elm.r1.ReplaceMatches element); + + org.opencds.cqf.cql.engine.elm.execution.TodayEvaluator map(org.hl7.elm.r1.Today element); + + org.opencds.cqf.cql.engine.elm.execution.ExistsEvaluator map(org.hl7.elm.r1.Exists element); + + org.cqframework.cql.elm.execution.SubsumedBy map(org.hl7.elm.r1.SubsumedBy element); + + org.opencds.cqf.cql.engine.elm.execution.RatioEvaluator map(org.hl7.elm.r1.Ratio element); + + org.opencds.cqf.cql.engine.elm.execution.EndEvaluator map(org.hl7.elm.r1.End element); + + org.opencds.cqf.cql.engine.elm.execution.ToLongEvaluator map(org.hl7.elm.r1.ToLong element); + + org.opencds.cqf.cql.engine.elm.execution.CountEvaluator map(org.hl7.elm.r1.Count element); + + org.opencds.cqf.cql.engine.elm.execution.CodeRefEvaluator map(org.hl7.elm.r1.CodeRef element); + + org.opencds.cqf.cql.engine.elm.execution.SumEvaluator map(org.hl7.elm.r1.Sum element); + + org.opencds.cqf.cql.engine.elm.execution.MinEvaluator map(org.hl7.elm.r1.Min element); + + org.cqframework.cql.elm.execution.Total map(org.hl7.elm.r1.Total element); + + org.cqframework.cql.elm.execution.IncludeElement map(org.hl7.elm.r1.IncludeElement element); + + org.opencds.cqf.cql.engine.elm.execution.ConceptEvaluator map(org.hl7.elm.r1.Concept element); + + org.opencds.cqf.cql.engine.elm.execution.DateTimeComponentFromEvaluator map( + org.hl7.elm.r1.DateTimeComponentFrom element); + + org.opencds.cqf.cql.engine.elm.execution.MedianEvaluator map(org.hl7.elm.r1.Median element); + + org.opencds.cqf.cql.engine.elm.execution.FlattenEvaluator map(org.hl7.elm.r1.Flatten element); + + org.opencds.cqf.cql.engine.elm.execution.ExpEvaluator map(org.hl7.elm.r1.Exp element); + + org.cqframework.cql.elm.execution.NamedTypeSpecifier map(org.hl7.elm.r1.NamedTypeSpecifier element); + + org.opencds.cqf.cql.engine.elm.execution.LessEvaluator map(org.hl7.elm.r1.Less element); + + org.opencds.cqf.cql.engine.elm.execution.IdentifierRefEvaluator map(org.hl7.elm.r1.IdentifierRef element); + + org.opencds.cqf.cql.engine.elm.execution.ToConceptEvaluator map(org.hl7.elm.r1.ToConcept element); + + org.opencds.cqf.cql.engine.elm.execution.PositionOfEvaluator map(org.hl7.elm.r1.PositionOf element); + + org.opencds.cqf.cql.engine.elm.execution.TimezoneFromEvaluator map(org.hl7.elm.r1.TimezoneFrom element); + + org.opencds.cqf.cql.engine.elm.execution.LnEvaluator map(org.hl7.elm.r1.Ln element); + + org.opencds.cqf.cql.engine.elm.execution.CalculateAgeAtEvaluator map(org.hl7.elm.r1.CalculateAgeAt element); + + org.opencds.cqf.cql.engine.elm.execution.LogEvaluator map(org.hl7.elm.r1.Log element); + + org.opencds.cqf.cql.engine.elm.execution.NowEvaluator map(org.hl7.elm.r1.Now element); + + org.opencds.cqf.cql.engine.elm.execution.ToDateTimeEvaluator map(org.hl7.elm.r1.ToDateTime element); + + org.opencds.cqf.cql.engine.elm.execution.QueryLetRefEvaluator map(org.hl7.elm.r1.QueryLetRef element); + + org.opencds.cqf.cql.engine.elm.execution.FirstEvaluator map(org.hl7.elm.r1.First element); + + org.cqframework.cql.elm.execution.Without map(org.hl7.elm.r1.Without element); + + org.opencds.cqf.cql.engine.elm.execution.LowBoundaryEvaluator map(org.hl7.elm.r1.LowBoundary element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertQuantityEvaluator map(org.hl7.elm.r1.ConvertQuantity element); + + org.cqframework.cql.elm.execution.CanConvertQuantity map(org.hl7.elm.r1.CanConvertQuantity element); + + org.opencds.cqf.cql.engine.elm.execution.MaxValueEvaluator map(org.hl7.elm.r1.MaxValue element); + + org.opencds.cqf.cql.engine.elm.execution.CoalesceEvaluator map(org.hl7.elm.r1.Coalesce element); + + org.opencds.cqf.cql.engine.elm.execution.ConceptRefEvaluator map(org.hl7.elm.r1.ConceptRef element); + + org.cqframework.cql.elm.execution.LetClause map(org.hl7.elm.r1.LetClause element); + + org.opencds.cqf.cql.engine.elm.execution.LiteralEvaluator map(org.hl7.elm.r1.Literal element); + + org.opencds.cqf.cql.engine.elm.execution.OperandRefEvaluator map(org.hl7.elm.r1.OperandRef element); + + org.opencds.cqf.cql.engine.elm.execution.SizeEvaluator map(org.hl7.elm.r1.Size element); + + org.opencds.cqf.cql.engine.elm.execution.UpperEvaluator map(org.hl7.elm.r1.Upper element); + + org.cqframework.cql.elm.execution.ValueSetDef map(org.hl7.elm.r1.ValueSetDef element); + + org.opencds.cqf.cql.engine.elm.execution.StartEvaluator map(org.hl7.elm.r1.Start element); + + org.opencds.cqf.cql.engine.elm.execution.CalculateAgeEvaluator map(org.hl7.elm.r1.CalculateAge element); + + org.cqframework.cql.elm.execution.ListTypeSpecifier map(org.hl7.elm.r1.ListTypeSpecifier element); + + org.opencds.cqf.cql.engine.elm.execution.PredecessorEvaluator map(org.hl7.elm.r1.Predecessor element); + + org.opencds.cqf.cql.engine.elm.execution.DescendentsEvaluator map(org.hl7.elm.r1.Descendents element); + + org.opencds.cqf.cql.engine.elm.execution.FilterEvaluator map(org.hl7.elm.r1.Filter element); + + org.opencds.cqf.cql.engine.elm.execution.FloorEvaluator map(org.hl7.elm.r1.Floor element); + + org.opencds.cqf.cql.engine.elm.execution.SameAsEvaluator map(org.hl7.elm.r1.SameAs element); + + org.cqframework.cql.elm.execution.Times map(org.hl7.elm.r1.Times element); + + org.opencds.cqf.cql.engine.elm.execution.UnionEvaluator map(org.hl7.elm.r1.Union element); + + org.opencds.cqf.cql.engine.elm.execution.AbsEvaluator map(org.hl7.elm.r1.Abs element); + + org.opencds.cqf.cql.engine.elm.execution.MeetsAfterEvaluator map(org.hl7.elm.r1.MeetsAfter element); + + org.opencds.cqf.cql.engine.elm.execution.ForEachEvaluator map(org.hl7.elm.r1.ForEach element); + + org.opencds.cqf.cql.engine.elm.execution.ListEvaluator map(org.hl7.elm.r1.List element); + + org.cqframework.cql.elm.execution.ByDirection map(org.hl7.elm.r1.ByDirection element); + + org.opencds.cqf.cql.engine.elm.execution.AfterEvaluator map(org.hl7.elm.r1.After element); + + org.opencds.cqf.cql.engine.elm.execution.SingletonFromEvaluator map(org.hl7.elm.r1.SingletonFrom element); + + org.opencds.cqf.cql.engine.elm.execution.ModuloEvaluator map(org.hl7.elm.r1.Modulo element); + + org.cqframework.cql.elm.execution.UsingDef map(org.hl7.elm.r1.UsingDef element); + + org.opencds.cqf.cql.engine.elm.execution.ToBooleanEvaluator map(org.hl7.elm.r1.ToBoolean element); + + org.opencds.cqf.cql.engine.elm.execution.ExpandEvaluator map(org.hl7.elm.r1.Expand element); + + org.opencds.cqf.cql.engine.elm.execution.PrecisionEvaluator map(org.hl7.elm.r1.Precision element); + + org.cqframework.cql.elm.execution.ConvertsToRatio map(org.hl7.elm.r1.ConvertsToRatio element); + + org.opencds.cqf.cql.engine.elm.execution.RoundEvaluator map(org.hl7.elm.r1.Round element); + + org.cqframework.cql.elm.execution.DateFilterElement map(org.hl7.elm.r1.DateFilterElement element); + + org.cqframework.cql.elm.execution.SortClause map(org.hl7.elm.r1.SortClause element); + + org.cqframework.cql.elm.execution.ToChars map(org.hl7.elm.r1.ToChars element); + + org.opencds.cqf.cql.engine.elm.execution.ProperIncludedInEvaluator map(org.hl7.elm.r1.ProperIncludedIn element); + + org.opencds.cqf.cql.engine.elm.execution.DateEvaluator map(org.hl7.elm.r1.Date element); + + org.opencds.cqf.cql.engine.elm.execution.WidthEvaluator map(org.hl7.elm.r1.Width element); + + org.opencds.cqf.cql.engine.elm.execution.TimezoneOffsetFromEvaluator map(org.hl7.elm.r1.TimezoneOffsetFrom element); + + org.opencds.cqf.cql.engine.elm.execution.SuccessorEvaluator map(org.hl7.elm.r1.Successor element); + + org.opencds.cqf.cql.engine.elm.execution.HighBoundaryEvaluator map(org.hl7.elm.r1.HighBoundary element); + + org.opencds.cqf.cql.engine.elm.execution.SubtractEvaluator map(org.hl7.elm.r1.Subtract element); + + org.cqframework.cql.elm.execution.CaseItem map(org.hl7.elm.r1.CaseItem element); + + org.opencds.cqf.cql.engine.elm.execution.EndsEvaluator map(org.hl7.elm.r1.Ends element); + + org.cqframework.cql.elm.execution.CodeDef map(org.hl7.elm.r1.CodeDef element); + + org.opencds.cqf.cql.engine.elm.execution.OverlapsAfterEvaluator map(org.hl7.elm.r1.OverlapsAfter element); + + org.opencds.cqf.cql.engine.elm.execution.OverlapsBeforeEvaluator map(org.hl7.elm.r1.OverlapsBefore element); + + org.opencds.cqf.cql.engine.elm.execution.GeometricMeanEvaluator map(org.hl7.elm.r1.GeometricMean element); + + org.opencds.cqf.cql.engine.elm.execution.LowerEvaluator map(org.hl7.elm.r1.Lower element); + + org.opencds.cqf.cql.engine.elm.execution.ExpressionRefEvaluator map(org.hl7.elm.r1.ExpressionRef element); + + org.opencds.cqf.cql.engine.elm.execution.TimeFromEvaluator map(org.hl7.elm.r1.TimeFrom element); + + org.cqframework.cql.elm.execution.Current map(org.hl7.elm.r1.Current element); + + org.opencds.cqf.cql.engine.elm.execution.ModeEvaluator map(org.hl7.elm.r1.Mode element); + + org.opencds.cqf.cql.engine.elm.execution.StartsWithEvaluator map(org.hl7.elm.r1.StartsWith element); + + org.opencds.cqf.cql.engine.elm.execution.AllTrueEvaluator map(org.hl7.elm.r1.AllTrue element); + + org.opencds.cqf.cql.engine.elm.execution.AnyTrueEvaluator map(org.hl7.elm.r1.AnyTrue element); + + org.cqframework.cql.elm.execution.Search map(org.hl7.elm.r1.Search element); + + org.opencds.cqf.cql.engine.elm.execution.SliceEvaluator map(org.hl7.elm.r1.Slice element); + + org.opencds.cqf.cql.engine.elm.execution.ToRatioEvaluator map(org.hl7.elm.r1.ToRatio element); + + org.opencds.cqf.cql.engine.elm.execution.TimeOfDayEvaluator map(org.hl7.elm.r1.TimeOfDay element); + + org.opencds.cqf.cql.engine.elm.execution.ProperInEvaluator map(org.hl7.elm.r1.ProperIn element); + + org.opencds.cqf.cql.engine.elm.execution.AndEvaluator map(org.hl7.elm.r1.And element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToIntegerEvaluator map(org.hl7.elm.r1.ConvertsToInteger element); + + org.opencds.cqf.cql.engine.elm.execution.AliasRefEvaluator map(org.hl7.elm.r1.AliasRef element); + + org.cqframework.cql.elm.execution.CodeSystemDef map(org.hl7.elm.r1.CodeSystemDef element); + + org.opencds.cqf.cql.engine.elm.execution.IntervalEvaluator map(org.hl7.elm.r1.Interval element); + + org.cqframework.cql.elm.execution.OtherFilterElement map(org.hl7.elm.r1.OtherFilterElement element); + + org.opencds.cqf.cql.engine.elm.execution.LastPositionOfEvaluator map(org.hl7.elm.r1.LastPositionOf element); + + org.opencds.cqf.cql.engine.elm.execution.IsTrueEvaluator map(org.hl7.elm.r1.IsTrue element); + + org.opencds.cqf.cql.engine.elm.execution.ExpandValueSetEvaluator map(org.hl7.elm.r1.ExpandValueSet element); + + org.opencds.cqf.cql.engine.elm.execution.NegateEvaluator map(org.hl7.elm.r1.Negate element); + + org.cqframework.cql.elm.execution.IntervalTypeSpecifier map(org.hl7.elm.r1.IntervalTypeSpecifier element); + + org.opencds.cqf.cql.engine.elm.execution.OverlapsEvaluator map(org.hl7.elm.r1.Overlaps element); + + org.opencds.cqf.cql.engine.elm.execution.DistinctEvaluator map(org.hl7.elm.r1.Distinct element); + + org.opencds.cqf.cql.engine.elm.execution.CodeSystemRefEvaluator map(org.hl7.elm.r1.CodeSystemRef element); + + org.opencds.cqf.cql.engine.elm.execution.InEvaluator map(org.hl7.elm.r1.In element); + + org.cqframework.cql.elm.execution.ChoiceTypeSpecifier map(org.hl7.elm.r1.ChoiceTypeSpecifier element); + + org.opencds.cqf.cql.engine.elm.execution.EndsWithEvaluator map(org.hl7.elm.r1.EndsWith element); + + org.cqframework.cql.elm.execution.ParameterDef map(org.hl7.elm.r1.ParameterDef element); + + org.opencds.cqf.cql.engine.elm.execution.IncludesEvaluator map(org.hl7.elm.r1.Includes element); + + org.opencds.cqf.cql.engine.elm.execution.ToTimeEvaluator map(org.hl7.elm.r1.ToTime element); + + org.opencds.cqf.cql.engine.elm.execution.IntersectEvaluator map(org.hl7.elm.r1.Intersect element); + + org.opencds.cqf.cql.engine.elm.execution.SameOrBeforeEvaluator map(org.hl7.elm.r1.SameOrBefore element); + + org.opencds.cqf.cql.engine.elm.execution.PopulationStdDevEvaluator map(org.hl7.elm.r1.PopulationStdDev element); + + org.opencds.cqf.cql.engine.elm.execution.CodeEvaluator map(org.hl7.elm.r1.Code element); + + org.opencds.cqf.cql.engine.elm.execution.ConcatenateEvaluator map(org.hl7.elm.r1.Concatenate element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertEvaluator map(org.hl7.elm.r1.Convert element); + + org.opencds.cqf.cql.engine.elm.execution.MinValueEvaluator map(org.hl7.elm.r1.MinValue element); + + org.opencds.cqf.cql.engine.elm.execution.SameOrAfterEvaluator map(org.hl7.elm.r1.SameOrAfter element); + + org.opencds.cqf.cql.engine.elm.execution.SplitEvaluator map(org.hl7.elm.r1.Split element); + + org.opencds.cqf.cql.engine.elm.execution.GreaterEvaluator map(org.hl7.elm.r1.Greater element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToLongEvaluator map(org.hl7.elm.r1.ConvertsToLong element); + + org.cqframework.cql.elm.execution.Aggregate map(org.hl7.elm.r1.Aggregate element); + + org.opencds.cqf.cql.engine.elm.execution.IfEvaluator map(org.hl7.elm.r1.If element); + + org.opencds.cqf.cql.engine.elm.execution.MaxEvaluator map(org.hl7.elm.r1.Max element); + + org.opencds.cqf.cql.engine.elm.execution.IsFalseEvaluator map(org.hl7.elm.r1.IsFalse element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToTimeEvaluator map(org.hl7.elm.r1.ConvertsToTime element); + + org.opencds.cqf.cql.engine.elm.execution.NotEqualEvaluator map(org.hl7.elm.r1.NotEqual element); + + org.opencds.cqf.cql.engine.elm.execution.ExceptEvaluator map(org.hl7.elm.r1.Except element); + + org.cqframework.cql.elm.execution.ReturnClause map(org.hl7.elm.r1.ReturnClause element); + + org.opencds.cqf.cql.engine.elm.execution.MatchesEvaluator map(org.hl7.elm.r1.Matches element); + + org.opencds.cqf.cql.engine.elm.execution.MultiplyEvaluator map(org.hl7.elm.r1.Multiply element); + + org.opencds.cqf.cql.engine.elm.execution.ToListEvaluator map(org.hl7.elm.r1.ToList element); + + org.opencds.cqf.cql.engine.elm.execution.IsNullEvaluator map(org.hl7.elm.r1.IsNull element); + + org.opencds.cqf.cql.engine.elm.execution.InstanceEvaluator map(org.hl7.elm.r1.Instance element); + + org.cqframework.cql.elm.execution.TupleTypeSpecifier map(org.hl7.elm.r1.TupleTypeSpecifier element); + + org.opencds.cqf.cql.engine.elm.execution.PopulationVarianceEvaluator map(org.hl7.elm.r1.PopulationVariance element); + + org.opencds.cqf.cql.engine.elm.execution.RepeatEvaluator map(org.hl7.elm.r1.Repeat element); + + org.cqframework.cql.elm.execution.Library map(org.hl7.elm.r1.Library element); + + org.opencds.cqf.cql.engine.elm.execution.IsEvaluator map(org.hl7.elm.r1.Is element); + + org.opencds.cqf.cql.engine.elm.execution.TruncateEvaluator map(org.hl7.elm.r1.Truncate element); + + org.opencds.cqf.cql.engine.elm.execution.ImpliesEvaluator map(org.hl7.elm.r1.Implies element); + + org.cqframework.cql.elm.execution.CodeFilterElement map(org.hl7.elm.r1.CodeFilterElement element); + + org.cqframework.cql.elm.execution.ByColumn map(org.hl7.elm.r1.ByColumn element); + + org.opencds.cqf.cql.engine.elm.execution.AsEvaluator map(org.hl7.elm.r1.As element); + + org.opencds.cqf.cql.engine.elm.execution.GreaterOrEqualEvaluator map(org.hl7.elm.r1.GreaterOrEqual element); + + org.opencds.cqf.cql.engine.elm.execution.StartsEvaluator map(org.hl7.elm.r1.Starts element); + + org.opencds.cqf.cql.engine.elm.execution.ChildrenEvaluator map(org.hl7.elm.r1.Children element); + + org.opencds.cqf.cql.engine.elm.execution.NotEvaluator map(org.hl7.elm.r1.Not element); + + org.opencds.cqf.cql.engine.elm.execution.DateFromEvaluator map(org.hl7.elm.r1.DateFrom element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToBooleanEvaluator map(org.hl7.elm.r1.ConvertsToBoolean element); + + org.opencds.cqf.cql.engine.elm.execution.MeetsBeforeEvaluator map(org.hl7.elm.r1.MeetsBefore element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToDateTimeEvaluator map(org.hl7.elm.r1.ConvertsToDateTime element); + + org.cqframework.cql.elm.execution.Iteration map(org.hl7.elm.r1.Iteration element); + + org.opencds.cqf.cql.engine.elm.execution.AvgEvaluator map(org.hl7.elm.r1.Avg element); + + org.opencds.cqf.cql.engine.elm.execution.DurationBetweenEvaluator map(org.hl7.elm.r1.DurationBetween element); + + org.opencds.cqf.cql.engine.elm.execution.ConvertsToQuantityEvaluator map(org.hl7.elm.r1.ConvertsToQuantity element); + + org.opencds.cqf.cql.engine.elm.execution.StdDevEvaluator map(org.hl7.elm.r1.StdDev element); + + org.opencds.cqf.cql.engine.elm.execution.MessageEvaluator map(org.hl7.elm.r1.Message element); + + org.opencds.cqf.cql.engine.elm.execution.ToIntegerEvaluator map(org.hl7.elm.r1.ToInteger element); + + org.opencds.cqf.cql.engine.elm.execution.IncludedInEvaluator map(org.hl7.elm.r1.IncludedIn element); + + org.cqframework.cql.elm.execution.FunctionDef map(org.hl7.elm.r1.FunctionDef element); + + org.cqframework.cql.elm.execution.Sort map(org.hl7.elm.r1.Sort element); + + org.opencds.cqf.cql.engine.elm.execution.SplitOnMatchesEvaluator map(org.hl7.elm.r1.SplitOnMatches element); + + org.opencds.cqf.cql.engine.elm.execution.CaseEvaluator map(org.hl7.elm.r1.Case element); + + org.cqframework.cql.elm.execution.ConceptDef map(org.hl7.elm.r1.ConceptDef element); + + org.opencds.cqf.cql.engine.elm.execution.LengthEvaluator map(org.hl7.elm.r1.Length element); + + org.opencds.cqf.cql.engine.elm.execution.DateTimeEvaluator map(org.hl7.elm.r1.DateTime element); + + org.opencds.cqf.cql.engine.elm.execution.ParameterRefEvaluator map(org.hl7.elm.r1.ParameterRef element); + + org.opencds.cqf.cql.engine.elm.execution.OrEvaluator map(org.hl7.elm.r1.Or element); + + org.opencds.cqf.cql.engine.elm.execution.TruncatedDivideEvaluator map(org.hl7.elm.r1.TruncatedDivide element); + + org.opencds.cqf.cql.engine.elm.execution.DifferenceBetweenEvaluator map(org.hl7.elm.r1.DifferenceBetween element); + + default org.cqframework.cql.elm.execution.SortByItem map(org.hl7.elm.r1.SortByItem element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.ByDirection) { + return map((org.hl7.elm.r1.ByDirection) element); + } else if (element instanceof org.hl7.elm.r1.ByExpression) { + return map((org.hl7.elm.r1.ByExpression) element); + } else if (element instanceof org.hl7.elm.r1.ByColumn) { + return map((org.hl7.elm.r1.ByColumn) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.SortByItem: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.AggregateExpression map(org.hl7.elm.r1.AggregateExpression element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.Sum) { + return map((org.hl7.elm.r1.Sum) element); + } else if (element instanceof org.hl7.elm.r1.Min) { + return map((org.hl7.elm.r1.Min) element); + } else if (element instanceof org.hl7.elm.r1.Count) { + return map((org.hl7.elm.r1.Count) element); + } else if (element instanceof org.hl7.elm.r1.Mode) { + return map((org.hl7.elm.r1.Mode) element); + } else if (element instanceof org.hl7.elm.r1.AllTrue) { + return map((org.hl7.elm.r1.AllTrue) element); + } else if (element instanceof org.hl7.elm.r1.PopulationVariance) { + return map((org.hl7.elm.r1.PopulationVariance) element); + } else if (element instanceof org.hl7.elm.r1.Avg) { + return map((org.hl7.elm.r1.Avg) element); + } else if (element instanceof org.hl7.elm.r1.StdDev) { + return map((org.hl7.elm.r1.StdDev) element); + } else if (element instanceof org.hl7.elm.r1.Product) { + return map((org.hl7.elm.r1.Product) element); + } else if (element instanceof org.hl7.elm.r1.Max) { + return map((org.hl7.elm.r1.Max) element); + } else if (element instanceof org.hl7.elm.r1.Variance) { + return map((org.hl7.elm.r1.Variance) element); + } else if (element instanceof org.hl7.elm.r1.GeometricMean) { + return map((org.hl7.elm.r1.GeometricMean) element); + } else if (element instanceof org.hl7.elm.r1.Aggregate) { + return map((org.hl7.elm.r1.Aggregate) element); + } else if (element instanceof org.hl7.elm.r1.Median) { + return map((org.hl7.elm.r1.Median) element); + } else if (element instanceof org.hl7.elm.r1.PopulationStdDev) { + return map((org.hl7.elm.r1.PopulationStdDev) element); + } else if (element instanceof org.hl7.elm.r1.AnyTrue) { + return map((org.hl7.elm.r1.AnyTrue) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.AggregateExpression: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.UnaryExpression map(org.hl7.elm.r1.UnaryExpression element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.ConvertsToBoolean) { + return map((org.hl7.elm.r1.ConvertsToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.ToString) { + return map((org.hl7.elm.r1.ToString) element); + } else if (element instanceof org.hl7.elm.r1.Exists) { + return map((org.hl7.elm.r1.Exists) element); + } else if (element instanceof org.hl7.elm.r1.CanConvert) { + return map((org.hl7.elm.r1.CanConvert) element); + } else if (element instanceof org.hl7.elm.r1.ToBoolean) { + return map((org.hl7.elm.r1.ToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToLong) { + return map((org.hl7.elm.r1.ConvertsToLong) element); + } else if (element instanceof org.hl7.elm.r1.Exp) { + return map((org.hl7.elm.r1.Exp) element); + } else if (element instanceof org.hl7.elm.r1.Flatten) { + return map((org.hl7.elm.r1.Flatten) element); + } else if (element instanceof org.hl7.elm.r1.Lower) { + return map((org.hl7.elm.r1.Lower) element); + } else if (element instanceof org.hl7.elm.r1.Precision) { + return map((org.hl7.elm.r1.Precision) element); + } else if (element instanceof org.hl7.elm.r1.Length) { + return map((org.hl7.elm.r1.Length) element); + } else if (element instanceof org.hl7.elm.r1.Successor) { + return map((org.hl7.elm.r1.Successor) element); + } else if (element instanceof org.hl7.elm.r1.Width) { + return map((org.hl7.elm.r1.Width) element); + } else if (element instanceof org.hl7.elm.r1.ToInteger) { + return map((org.hl7.elm.r1.ToInteger) element); + } else if (element instanceof org.hl7.elm.r1.ToChars) { + return map((org.hl7.elm.r1.ToChars) element); + } else if (element instanceof org.hl7.elm.r1.SingletonFrom) { + return map((org.hl7.elm.r1.SingletonFrom) element); + } else if (element instanceof org.hl7.elm.r1.Size) { + return map((org.hl7.elm.r1.Size) element); + } else if (element instanceof org.hl7.elm.r1.As) { + return map((org.hl7.elm.r1.As) element); + } else if (element instanceof org.hl7.elm.r1.ToRatio) { + return map((org.hl7.elm.r1.ToRatio) element); + } else if (element instanceof org.hl7.elm.r1.Abs) { + return map((org.hl7.elm.r1.Abs) element); + } else if (element instanceof org.hl7.elm.r1.ToList) { + return map((org.hl7.elm.r1.ToList) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAge) { + return map((org.hl7.elm.r1.CalculateAge) element); + } else if (element instanceof org.hl7.elm.r1.ToConcept) { + return map((org.hl7.elm.r1.ToConcept) element); + } else if (element instanceof org.hl7.elm.r1.DateFrom) { + return map((org.hl7.elm.r1.DateFrom) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToInteger) { + return map((org.hl7.elm.r1.ConvertsToInteger) element); + } else if (element instanceof org.hl7.elm.r1.ToLong) { + return map((org.hl7.elm.r1.ToLong) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneFrom) { + return map((org.hl7.elm.r1.TimezoneFrom) element); + } else if (element instanceof org.hl7.elm.r1.Convert) { + return map((org.hl7.elm.r1.Convert) element); + } else if (element instanceof org.hl7.elm.r1.End) { + return map((org.hl7.elm.r1.End) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDate) { + return map((org.hl7.elm.r1.ConvertsToDate) element); + } else if (element instanceof org.hl7.elm.r1.Not) { + return map((org.hl7.elm.r1.Not) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDateTime) { + return map((org.hl7.elm.r1.ConvertsToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.ToDecimal) { + return map((org.hl7.elm.r1.ToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.Start) { + return map((org.hl7.elm.r1.Start) element); + } else if (element instanceof org.hl7.elm.r1.IsFalse) { + return map((org.hl7.elm.r1.IsFalse) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDecimal) { + return map((org.hl7.elm.r1.ConvertsToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.IsNull) { + return map((org.hl7.elm.r1.IsNull) element); + } else if (element instanceof org.hl7.elm.r1.IsTrue) { + return map((org.hl7.elm.r1.IsTrue) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToRatio) { + return map((org.hl7.elm.r1.ConvertsToRatio) element); + } else if (element instanceof org.hl7.elm.r1.Ceiling) { + return map((org.hl7.elm.r1.Ceiling) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneOffsetFrom) { + return map((org.hl7.elm.r1.TimezoneOffsetFrom) element); + } else if (element instanceof org.hl7.elm.r1.DateTimeComponentFrom) { + return map((org.hl7.elm.r1.DateTimeComponentFrom) element); + } else if (element instanceof org.hl7.elm.r1.ToQuantity) { + return map((org.hl7.elm.r1.ToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Upper) { + return map((org.hl7.elm.r1.Upper) element); + } else if (element instanceof org.hl7.elm.r1.Is) { + return map((org.hl7.elm.r1.Is) element); + } else if (element instanceof org.hl7.elm.r1.ExpandValueSet) { + return map((org.hl7.elm.r1.ExpandValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Truncate) { + return map((org.hl7.elm.r1.Truncate) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToString) { + return map((org.hl7.elm.r1.ConvertsToString) element); + } else if (element instanceof org.hl7.elm.r1.ToTime) { + return map((org.hl7.elm.r1.ToTime) element); + } else if (element instanceof org.hl7.elm.r1.Ln) { + return map((org.hl7.elm.r1.Ln) element); + } else if (element instanceof org.hl7.elm.r1.ToDate) { + return map((org.hl7.elm.r1.ToDate) element); + } else if (element instanceof org.hl7.elm.r1.TimeFrom) { + return map((org.hl7.elm.r1.TimeFrom) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToTime) { + return map((org.hl7.elm.r1.ConvertsToTime) element); + } else if (element instanceof org.hl7.elm.r1.Predecessor) { + return map((org.hl7.elm.r1.Predecessor) element); + } else if (element instanceof org.hl7.elm.r1.Negate) { + return map((org.hl7.elm.r1.Negate) element); + } else if (element instanceof org.hl7.elm.r1.PointFrom) { + return map((org.hl7.elm.r1.PointFrom) element); + } else if (element instanceof org.hl7.elm.r1.Distinct) { + return map((org.hl7.elm.r1.Distinct) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToQuantity) { + return map((org.hl7.elm.r1.ConvertsToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.ToDateTime) { + return map((org.hl7.elm.r1.ToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.Floor) { + return map((org.hl7.elm.r1.Floor) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.UnaryExpression: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.RelationshipClause map(org.hl7.elm.r1.RelationshipClause element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.Without) { + return map((org.hl7.elm.r1.Without) element); + } else if (element instanceof org.hl7.elm.r1.With) { + return map((org.hl7.elm.r1.With) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.RelationshipClause: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.Expression map(org.hl7.elm.r1.Expression element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.First) { + return map((org.hl7.elm.r1.First) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToBoolean) { + return map((org.hl7.elm.r1.ConvertsToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.CanConvertQuantity) { + return map((org.hl7.elm.r1.CanConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Meets) { + return map((org.hl7.elm.r1.Meets) element); + } else if (element instanceof org.hl7.elm.r1.ToString) { + return map((org.hl7.elm.r1.ToString) element); + } else if (element instanceof org.hl7.elm.r1.MinValue) { + return map((org.hl7.elm.r1.MinValue) element); + } else if (element instanceof org.hl7.elm.r1.DifferenceBetween) { + return map((org.hl7.elm.r1.DifferenceBetween) element); + } else if (element instanceof org.hl7.elm.r1.Exists) { + return map((org.hl7.elm.r1.Exists) element); + } else if (element instanceof org.hl7.elm.r1.CanConvert) { + return map((org.hl7.elm.r1.CanConvert) element); + } else if (element instanceof org.hl7.elm.r1.IncludedIn) { + return map((org.hl7.elm.r1.IncludedIn) element); + } else if (element instanceof org.hl7.elm.r1.Or) { + return map((org.hl7.elm.r1.Or) element); + } else if (element instanceof org.hl7.elm.r1.Current) { + return map((org.hl7.elm.r1.Current) element); + } else if (element instanceof org.hl7.elm.r1.Substring) { + return map((org.hl7.elm.r1.Substring) element); + } else if (element instanceof org.hl7.elm.r1.ToBoolean) { + return map((org.hl7.elm.r1.ToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToLong) { + return map((org.hl7.elm.r1.ConvertsToLong) element); + } else if (element instanceof org.hl7.elm.r1.Slice) { + return map((org.hl7.elm.r1.Slice) element); + } else if (element instanceof org.hl7.elm.r1.LessOrEqual) { + return map((org.hl7.elm.r1.LessOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.Exp) { + return map((org.hl7.elm.r1.Exp) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsAfter) { + return map((org.hl7.elm.r1.OverlapsAfter) element); + } else if (element instanceof org.hl7.elm.r1.Repeat) { + return map((org.hl7.elm.r1.Repeat) element); + } else if (element instanceof org.hl7.elm.r1.CodeSystemRef) { + return map((org.hl7.elm.r1.CodeSystemRef) element); + } else if (element instanceof org.hl7.elm.r1.Flatten) { + return map((org.hl7.elm.r1.Flatten) element); + } else if (element instanceof org.hl7.elm.r1.Lower) { + return map((org.hl7.elm.r1.Lower) element); + } else if (element instanceof org.hl7.elm.r1.Precision) { + return map((org.hl7.elm.r1.Precision) element); + } else if (element instanceof org.hl7.elm.r1.IndexOf) { + return map((org.hl7.elm.r1.IndexOf) element); + } else if (element instanceof org.hl7.elm.r1.ProperContains) { + return map((org.hl7.elm.r1.ProperContains) element); + } else if (element instanceof org.hl7.elm.r1.Length) { + return map((org.hl7.elm.r1.Length) element); + } else if (element instanceof org.hl7.elm.r1.PositionOf) { + return map((org.hl7.elm.r1.PositionOf) element); + } else if (element instanceof org.hl7.elm.r1.Code) { + return map((org.hl7.elm.r1.Code) element); + } else if (element instanceof org.hl7.elm.r1.Successor) { + return map((org.hl7.elm.r1.Successor) element); + } else if (element instanceof org.hl7.elm.r1.SplitOnMatches) { + return map((org.hl7.elm.r1.SplitOnMatches) element); + } else if (element instanceof org.hl7.elm.r1.Retrieve) { + return map((org.hl7.elm.r1.Retrieve) element); + } else if (element instanceof org.hl7.elm.r1.Width) { + return map((org.hl7.elm.r1.Width) element); + } else if (element instanceof org.hl7.elm.r1.ToInteger) { + return map((org.hl7.elm.r1.ToInteger) element); + } else if (element instanceof org.hl7.elm.r1.SameAs) { + return map((org.hl7.elm.r1.SameAs) element); + } else if (element instanceof org.hl7.elm.r1.Descendents) { + return map((org.hl7.elm.r1.Descendents) element); + } else if (element instanceof org.hl7.elm.r1.ToChars) { + return map((org.hl7.elm.r1.ToChars) element); + } else if (element instanceof org.hl7.elm.r1.SingletonFrom) { + return map((org.hl7.elm.r1.SingletonFrom) element); + } else if (element instanceof org.hl7.elm.r1.InCodeSystem) { + return map((org.hl7.elm.r1.InCodeSystem) element); + } else if (element instanceof org.hl7.elm.r1.Sum) { + return map((org.hl7.elm.r1.Sum) element); + } else if (element instanceof org.hl7.elm.r1.Size) { + return map((org.hl7.elm.r1.Size) element); + } else if (element instanceof org.hl7.elm.r1.Interval) { + return map((org.hl7.elm.r1.Interval) element); + } else if (element instanceof org.hl7.elm.r1.EndsWith) { + return map((org.hl7.elm.r1.EndsWith) element); + } else if (element instanceof org.hl7.elm.r1.Tuple) { + return map((org.hl7.elm.r1.Tuple) element); + } else if (element instanceof org.hl7.elm.r1.As) { + return map((org.hl7.elm.r1.As) element); + } else if (element instanceof org.hl7.elm.r1.ValueSetRef) { + return map((org.hl7.elm.r1.ValueSetRef) element); + } else if (element instanceof org.hl7.elm.r1.Xor) { + return map((org.hl7.elm.r1.Xor) element); + } else if (element instanceof org.hl7.elm.r1.Combine) { + return map((org.hl7.elm.r1.Combine) element); + } else if (element instanceof org.hl7.elm.r1.Starts) { + return map((org.hl7.elm.r1.Starts) element); + } else if (element instanceof org.hl7.elm.r1.TimeOfDay) { + return map((org.hl7.elm.r1.TimeOfDay) element); + } else if (element instanceof org.hl7.elm.r1.Min) { + return map((org.hl7.elm.r1.Min) element); + } else if (element instanceof org.hl7.elm.r1.Power) { + return map((org.hl7.elm.r1.Power) element); + } else if (element instanceof org.hl7.elm.r1.AnyInValueSet) { + return map((org.hl7.elm.r1.AnyInValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Count) { + return map((org.hl7.elm.r1.Count) element); + } else if (element instanceof org.hl7.elm.r1.Query) { + return map((org.hl7.elm.r1.Query) element); + } else if (element instanceof org.hl7.elm.r1.Sort) { + return map((org.hl7.elm.r1.Sort) element); + } else if (element instanceof org.hl7.elm.r1.Mode) { + return map((org.hl7.elm.r1.Mode) element); + } else if (element instanceof org.hl7.elm.r1.Equal) { + return map((org.hl7.elm.r1.Equal) element); + } else if (element instanceof org.hl7.elm.r1.Date) { + return map((org.hl7.elm.r1.Date) element); + } else if (element instanceof org.hl7.elm.r1.ForEach) { + return map((org.hl7.elm.r1.ForEach) element); + } else if (element instanceof org.hl7.elm.r1.Intersect) { + return map((org.hl7.elm.r1.Intersect) element); + } else if (element instanceof org.hl7.elm.r1.ToRatio) { + return map((org.hl7.elm.r1.ToRatio) element); + } else if (element instanceof org.hl7.elm.r1.Abs) { + return map((org.hl7.elm.r1.Abs) element); + } else if (element instanceof org.hl7.elm.r1.ConceptRef) { + return map((org.hl7.elm.r1.ConceptRef) element); + } else if (element instanceof org.hl7.elm.r1.IdentifierRef) { + return map((org.hl7.elm.r1.IdentifierRef) element); + } else if (element instanceof org.hl7.elm.r1.Subsumes) { + return map((org.hl7.elm.r1.Subsumes) element); + } else if (element instanceof org.hl7.elm.r1.ToList) { + return map((org.hl7.elm.r1.ToList) element); + } else if (element instanceof org.hl7.elm.r1.ParameterRef) { + return map((org.hl7.elm.r1.ParameterRef) element); + } else if (element instanceof org.hl7.elm.r1.GreaterOrEqual) { + return map((org.hl7.elm.r1.GreaterOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.SubsumedBy) { + return map((org.hl7.elm.r1.SubsumedBy) element); + } else if (element instanceof org.hl7.elm.r1.Add) { + return map((org.hl7.elm.r1.Add) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAge) { + return map((org.hl7.elm.r1.CalculateAge) element); + } else if (element instanceof org.hl7.elm.r1.ToConcept) { + return map((org.hl7.elm.r1.ToConcept) element); + } else if (element instanceof org.hl7.elm.r1.Multiply) { + return map((org.hl7.elm.r1.Multiply) element); + } else if (element instanceof org.hl7.elm.r1.DateFrom) { + return map((org.hl7.elm.r1.DateFrom) element); + } else if (element instanceof org.hl7.elm.r1.SameOrAfter) { + return map((org.hl7.elm.r1.SameOrAfter) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToInteger) { + return map((org.hl7.elm.r1.ConvertsToInteger) element); + } else if (element instanceof org.hl7.elm.r1.ToLong) { + return map((org.hl7.elm.r1.ToLong) element); + } else if (element instanceof org.hl7.elm.r1.AllTrue) { + return map((org.hl7.elm.r1.AllTrue) element); + } else if (element instanceof org.hl7.elm.r1.Coalesce) { + return map((org.hl7.elm.r1.Coalesce) element); + } else if (element instanceof org.hl7.elm.r1.FunctionRef) { + return map((org.hl7.elm.r1.FunctionRef) element); + } else if (element instanceof org.hl7.elm.r1.Expand) { + return map((org.hl7.elm.r1.Expand) element); + } else if (element instanceof org.hl7.elm.r1.PopulationVariance) { + return map((org.hl7.elm.r1.PopulationVariance) element); + } else if (element instanceof org.hl7.elm.r1.Filter) { + return map((org.hl7.elm.r1.Filter) element); + } else if (element instanceof org.hl7.elm.r1.ProperIn) { + return map((org.hl7.elm.r1.ProperIn) element); + } else if (element instanceof org.hl7.elm.r1.StartsWith) { + return map((org.hl7.elm.r1.StartsWith) element); + } else if (element instanceof org.hl7.elm.r1.Null) { + return map((org.hl7.elm.r1.Null) element); + } else if (element instanceof org.hl7.elm.r1.LowBoundary) { + return map((org.hl7.elm.r1.LowBoundary) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneFrom) { + return map((org.hl7.elm.r1.TimezoneFrom) element); + } else if (element instanceof org.hl7.elm.r1.Convert) { + return map((org.hl7.elm.r1.Convert) element); + } else if (element instanceof org.hl7.elm.r1.Less) { + return map((org.hl7.elm.r1.Less) element); + } else if (element instanceof org.hl7.elm.r1.Search) { + return map((org.hl7.elm.r1.Search) element); + } else if (element instanceof org.hl7.elm.r1.End) { + return map((org.hl7.elm.r1.End) element); + } else if (element instanceof org.hl7.elm.r1.QueryLetRef) { + return map((org.hl7.elm.r1.QueryLetRef) element); + } else if (element instanceof org.hl7.elm.r1.Now) { + return map((org.hl7.elm.r1.Now) element); + } else if (element instanceof org.hl7.elm.r1.Concatenate) { + return map((org.hl7.elm.r1.Concatenate) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDate) { + return map((org.hl7.elm.r1.ConvertsToDate) element); + } else if (element instanceof org.hl7.elm.r1.ConvertQuantity) { + return map((org.hl7.elm.r1.ConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Avg) { + return map((org.hl7.elm.r1.Avg) element); + } else if (element instanceof org.hl7.elm.r1.Not) { + return map((org.hl7.elm.r1.Not) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDateTime) { + return map((org.hl7.elm.r1.ConvertsToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.Includes) { + return map((org.hl7.elm.r1.Includes) element); + } else if (element instanceof org.hl7.elm.r1.ToDecimal) { + return map((org.hl7.elm.r1.ToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.Start) { + return map((org.hl7.elm.r1.Start) element); + } else if (element instanceof org.hl7.elm.r1.IsFalse) { + return map((org.hl7.elm.r1.IsFalse) element); + } else if (element instanceof org.hl7.elm.r1.Modulo) { + return map((org.hl7.elm.r1.Modulo) element); + } else if (element instanceof org.hl7.elm.r1.Collapse) { + return map((org.hl7.elm.r1.Collapse) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDecimal) { + return map((org.hl7.elm.r1.ConvertsToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.Case) { + return map((org.hl7.elm.r1.Case) element); + } else if (element instanceof org.hl7.elm.r1.Instance) { + return map((org.hl7.elm.r1.Instance) element); + } else if (element instanceof org.hl7.elm.r1.IsNull) { + return map((org.hl7.elm.r1.IsNull) element); + } else if (element instanceof org.hl7.elm.r1.StdDev) { + return map((org.hl7.elm.r1.StdDev) element); + } else if (element instanceof org.hl7.elm.r1.IsTrue) { + return map((org.hl7.elm.r1.IsTrue) element); + } else if (element instanceof org.hl7.elm.r1.If) { + return map((org.hl7.elm.r1.If) element); + } else if (element instanceof org.hl7.elm.r1.Product) { + return map((org.hl7.elm.r1.Product) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToRatio) { + return map((org.hl7.elm.r1.ConvertsToRatio) element); + } else if (element instanceof org.hl7.elm.r1.CodeRef) { + return map((org.hl7.elm.r1.CodeRef) element); + } else if (element instanceof org.hl7.elm.r1.ReplaceMatches) { + return map((org.hl7.elm.r1.ReplaceMatches) element); + } else if (element instanceof org.hl7.elm.r1.MeetsAfter) { + return map((org.hl7.elm.r1.MeetsAfter) element); + } else if (element instanceof org.hl7.elm.r1.Overlaps) { + return map((org.hl7.elm.r1.Overlaps) element); + } else if (element instanceof org.hl7.elm.r1.Ceiling) { + return map((org.hl7.elm.r1.Ceiling) element); + } else if (element instanceof org.hl7.elm.r1.AnyInCodeSystem) { + return map((org.hl7.elm.r1.AnyInCodeSystem) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneOffsetFrom) { + return map((org.hl7.elm.r1.TimezoneOffsetFrom) element); + } else if (element instanceof org.hl7.elm.r1.Indexer) { + return map((org.hl7.elm.r1.Indexer) element); + } else if (element instanceof org.hl7.elm.r1.Message) { + return map((org.hl7.elm.r1.Message) element); + } else if (element instanceof org.hl7.elm.r1.Union) { + return map((org.hl7.elm.r1.Union) element); + } else if (element instanceof org.hl7.elm.r1.NotEqual) { + return map((org.hl7.elm.r1.NotEqual) element); + } else if (element instanceof org.hl7.elm.r1.Children) { + return map((org.hl7.elm.r1.Children) element); + } else if (element instanceof org.hl7.elm.r1.Ends) { + return map((org.hl7.elm.r1.Ends) element); + } else if (element instanceof org.hl7.elm.r1.DateTimeComponentFrom) { + return map((org.hl7.elm.r1.DateTimeComponentFrom) element); + } else if (element instanceof org.hl7.elm.r1.Iteration) { + return map((org.hl7.elm.r1.Iteration) element); + } else if (element instanceof org.hl7.elm.r1.Except) { + return map((org.hl7.elm.r1.Except) element); + } else if (element instanceof org.hl7.elm.r1.Ratio) { + return map((org.hl7.elm.r1.Ratio) element); + } else if (element instanceof org.hl7.elm.r1.Quantity) { + return map((org.hl7.elm.r1.Quantity) element); + } else if (element instanceof org.hl7.elm.r1.TruncatedDivide) { + return map((org.hl7.elm.r1.TruncatedDivide) element); + } else if (element instanceof org.hl7.elm.r1.ToQuantity) { + return map((org.hl7.elm.r1.ToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Upper) { + return map((org.hl7.elm.r1.Upper) element); + } else if (element instanceof org.hl7.elm.r1.SameOrBefore) { + return map((org.hl7.elm.r1.SameOrBefore) element); + } else if (element instanceof org.hl7.elm.r1.In) { + return map((org.hl7.elm.r1.In) element); + } else if (element instanceof org.hl7.elm.r1.MeetsBefore) { + return map((org.hl7.elm.r1.MeetsBefore) element); + } else if (element instanceof org.hl7.elm.r1.Log) { + return map((org.hl7.elm.r1.Log) element); + } else if (element instanceof org.hl7.elm.r1.Is) { + return map((org.hl7.elm.r1.Is) element); + } else if (element instanceof org.hl7.elm.r1.ExpandValueSet) { + return map((org.hl7.elm.r1.ExpandValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Greater) { + return map((org.hl7.elm.r1.Greater) element); + } else if (element instanceof org.hl7.elm.r1.Truncate) { + return map((org.hl7.elm.r1.Truncate) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludes) { + return map((org.hl7.elm.r1.ProperIncludes) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToString) { + return map((org.hl7.elm.r1.ConvertsToString) element); + } else if (element instanceof org.hl7.elm.r1.Divide) { + return map((org.hl7.elm.r1.Divide) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsBefore) { + return map((org.hl7.elm.r1.OverlapsBefore) element); + } else if (element instanceof org.hl7.elm.r1.After) { + return map((org.hl7.elm.r1.After) element); + } else if (element instanceof org.hl7.elm.r1.ToTime) { + return map((org.hl7.elm.r1.ToTime) element); + } else if (element instanceof org.hl7.elm.r1.HighBoundary) { + return map((org.hl7.elm.r1.HighBoundary) element); + } else if (element instanceof org.hl7.elm.r1.Ln) { + return map((org.hl7.elm.r1.Ln) element); + } else if (element instanceof org.hl7.elm.r1.ToDate) { + return map((org.hl7.elm.r1.ToDate) element); + } else if (element instanceof org.hl7.elm.r1.TimeFrom) { + return map((org.hl7.elm.r1.TimeFrom) element); + } else if (element instanceof org.hl7.elm.r1.Subtract) { + return map((org.hl7.elm.r1.Subtract) element); + } else if (element instanceof org.hl7.elm.r1.List) { + return map((org.hl7.elm.r1.List) element); + } else if (element instanceof org.hl7.elm.r1.Max) { + return map((org.hl7.elm.r1.Max) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToTime) { + return map((org.hl7.elm.r1.ConvertsToTime) element); + } else if (element instanceof org.hl7.elm.r1.Predecessor) { + return map((org.hl7.elm.r1.Predecessor) element); + } else if (element instanceof org.hl7.elm.r1.Negate) { + return map((org.hl7.elm.r1.Negate) element); + } else if (element instanceof org.hl7.elm.r1.Before) { + return map((org.hl7.elm.r1.Before) element); + } else if (element instanceof org.hl7.elm.r1.Implies) { + return map((org.hl7.elm.r1.Implies) element); + } else if (element instanceof org.hl7.elm.r1.PointFrom) { + return map((org.hl7.elm.r1.PointFrom) element); + } else if (element instanceof org.hl7.elm.r1.Variance) { + return map((org.hl7.elm.r1.Variance) element); + } else if (element instanceof org.hl7.elm.r1.Time) { + return map((org.hl7.elm.r1.Time) element); + } else if (element instanceof org.hl7.elm.r1.ExpressionRef) { + return map((org.hl7.elm.r1.ExpressionRef) element); + } else if (element instanceof org.hl7.elm.r1.Distinct) { + return map((org.hl7.elm.r1.Distinct) element); + } else if (element instanceof org.hl7.elm.r1.DurationBetween) { + return map((org.hl7.elm.r1.DurationBetween) element); + } else if (element instanceof org.hl7.elm.r1.Literal) { + return map((org.hl7.elm.r1.Literal) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToQuantity) { + return map((org.hl7.elm.r1.ConvertsToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Concept) { + return map((org.hl7.elm.r1.Concept) element); + } else if (element instanceof org.hl7.elm.r1.GeometricMean) { + return map((org.hl7.elm.r1.GeometricMean) element); + } else if (element instanceof org.hl7.elm.r1.Split) { + return map((org.hl7.elm.r1.Split) element); + } else if (element instanceof org.hl7.elm.r1.Aggregate) { + return map((org.hl7.elm.r1.Aggregate) element); + } else if (element instanceof org.hl7.elm.r1.ToDateTime) { + return map((org.hl7.elm.r1.ToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.Today) { + return map((org.hl7.elm.r1.Today) element); + } else if (element instanceof org.hl7.elm.r1.Median) { + return map((org.hl7.elm.r1.Median) element); + } else if (element instanceof org.hl7.elm.r1.Equivalent) { + return map((org.hl7.elm.r1.Equivalent) element); + } else if (element instanceof org.hl7.elm.r1.Round) { + return map((org.hl7.elm.r1.Round) element); + } else if (element instanceof org.hl7.elm.r1.DateTime) { + return map((org.hl7.elm.r1.DateTime) element); + } else if (element instanceof org.hl7.elm.r1.MaxValue) { + return map((org.hl7.elm.r1.MaxValue) element); + } else if (element instanceof org.hl7.elm.r1.InValueSet) { + return map((org.hl7.elm.r1.InValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Floor) { + return map((org.hl7.elm.r1.Floor) element); + } else if (element instanceof org.hl7.elm.r1.PopulationStdDev) { + return map((org.hl7.elm.r1.PopulationStdDev) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAgeAt) { + return map((org.hl7.elm.r1.CalculateAgeAt) element); + } else if (element instanceof org.hl7.elm.r1.AliasRef) { + return map((org.hl7.elm.r1.AliasRef) element); + } else if (element instanceof org.hl7.elm.r1.LastPositionOf) { + return map((org.hl7.elm.r1.LastPositionOf) element); + } else if (element instanceof org.hl7.elm.r1.Last) { + return map((org.hl7.elm.r1.Last) element); + } else if (element instanceof org.hl7.elm.r1.Matches) { + return map((org.hl7.elm.r1.Matches) element); + } else if (element instanceof org.hl7.elm.r1.Times) { + return map((org.hl7.elm.r1.Times) element); + } else if (element instanceof org.hl7.elm.r1.Total) { + return map((org.hl7.elm.r1.Total) element); + } else if (element instanceof org.hl7.elm.r1.Contains) { + return map((org.hl7.elm.r1.Contains) element); + } else if (element instanceof org.hl7.elm.r1.OperandRef) { + return map((org.hl7.elm.r1.OperandRef) element); + } else if (element instanceof org.hl7.elm.r1.And) { + return map((org.hl7.elm.r1.And) element); + } else if (element instanceof org.hl7.elm.r1.AnyTrue) { + return map((org.hl7.elm.r1.AnyTrue) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludedIn) { + return map((org.hl7.elm.r1.ProperIncludedIn) element); + } else if (element instanceof org.hl7.elm.r1.Property) { + return map((org.hl7.elm.r1.Property) element); + } else if (element instanceof org.hl7.elm.r1.AggregateExpression) { + return map((org.hl7.elm.r1.AggregateExpression) element); + } else if (element instanceof org.hl7.elm.r1.UnaryExpression) { + return map((org.hl7.elm.r1.UnaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.NaryExpression) { + return map((org.hl7.elm.r1.NaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.OperatorExpression) { + return map((org.hl7.elm.r1.OperatorExpression) element); + } else if (element instanceof org.hl7.elm.r1.TernaryExpression) { + return map((org.hl7.elm.r1.TernaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.BinaryExpression) { + return map((org.hl7.elm.r1.BinaryExpression) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.Expression: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.NaryExpression map(org.hl7.elm.r1.NaryExpression element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.Intersect) { + return map((org.hl7.elm.r1.Intersect) element); + } else if (element instanceof org.hl7.elm.r1.Coalesce) { + return map((org.hl7.elm.r1.Coalesce) element); + } else if (element instanceof org.hl7.elm.r1.Concatenate) { + return map((org.hl7.elm.r1.Concatenate) element); + } else if (element instanceof org.hl7.elm.r1.Union) { + return map((org.hl7.elm.r1.Union) element); + } else if (element instanceof org.hl7.elm.r1.Except) { + return map((org.hl7.elm.r1.Except) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.NaryExpression: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.OperatorExpression map(org.hl7.elm.r1.OperatorExpression element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.First) { + return map((org.hl7.elm.r1.First) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToBoolean) { + return map((org.hl7.elm.r1.ConvertsToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.CanConvertQuantity) { + return map((org.hl7.elm.r1.CanConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Meets) { + return map((org.hl7.elm.r1.Meets) element); + } else if (element instanceof org.hl7.elm.r1.ToString) { + return map((org.hl7.elm.r1.ToString) element); + } else if (element instanceof org.hl7.elm.r1.DifferenceBetween) { + return map((org.hl7.elm.r1.DifferenceBetween) element); + } else if (element instanceof org.hl7.elm.r1.Exists) { + return map((org.hl7.elm.r1.Exists) element); + } else if (element instanceof org.hl7.elm.r1.CanConvert) { + return map((org.hl7.elm.r1.CanConvert) element); + } else if (element instanceof org.hl7.elm.r1.IncludedIn) { + return map((org.hl7.elm.r1.IncludedIn) element); + } else if (element instanceof org.hl7.elm.r1.Or) { + return map((org.hl7.elm.r1.Or) element); + } else if (element instanceof org.hl7.elm.r1.Substring) { + return map((org.hl7.elm.r1.Substring) element); + } else if (element instanceof org.hl7.elm.r1.ToBoolean) { + return map((org.hl7.elm.r1.ToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToLong) { + return map((org.hl7.elm.r1.ConvertsToLong) element); + } else if (element instanceof org.hl7.elm.r1.Slice) { + return map((org.hl7.elm.r1.Slice) element); + } else if (element instanceof org.hl7.elm.r1.LessOrEqual) { + return map((org.hl7.elm.r1.LessOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.Exp) { + return map((org.hl7.elm.r1.Exp) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsAfter) { + return map((org.hl7.elm.r1.OverlapsAfter) element); + } else if (element instanceof org.hl7.elm.r1.Flatten) { + return map((org.hl7.elm.r1.Flatten) element); + } else if (element instanceof org.hl7.elm.r1.Lower) { + return map((org.hl7.elm.r1.Lower) element); + } else if (element instanceof org.hl7.elm.r1.Precision) { + return map((org.hl7.elm.r1.Precision) element); + } else if (element instanceof org.hl7.elm.r1.IndexOf) { + return map((org.hl7.elm.r1.IndexOf) element); + } else if (element instanceof org.hl7.elm.r1.ProperContains) { + return map((org.hl7.elm.r1.ProperContains) element); + } else if (element instanceof org.hl7.elm.r1.Length) { + return map((org.hl7.elm.r1.Length) element); + } else if (element instanceof org.hl7.elm.r1.PositionOf) { + return map((org.hl7.elm.r1.PositionOf) element); + } else if (element instanceof org.hl7.elm.r1.Successor) { + return map((org.hl7.elm.r1.Successor) element); + } else if (element instanceof org.hl7.elm.r1.SplitOnMatches) { + return map((org.hl7.elm.r1.SplitOnMatches) element); + } else if (element instanceof org.hl7.elm.r1.Width) { + return map((org.hl7.elm.r1.Width) element); + } else if (element instanceof org.hl7.elm.r1.ToInteger) { + return map((org.hl7.elm.r1.ToInteger) element); + } else if (element instanceof org.hl7.elm.r1.SameAs) { + return map((org.hl7.elm.r1.SameAs) element); + } else if (element instanceof org.hl7.elm.r1.Descendents) { + return map((org.hl7.elm.r1.Descendents) element); + } else if (element instanceof org.hl7.elm.r1.ToChars) { + return map((org.hl7.elm.r1.ToChars) element); + } else if (element instanceof org.hl7.elm.r1.SingletonFrom) { + return map((org.hl7.elm.r1.SingletonFrom) element); + } else if (element instanceof org.hl7.elm.r1.InCodeSystem) { + return map((org.hl7.elm.r1.InCodeSystem) element); + } else if (element instanceof org.hl7.elm.r1.Size) { + return map((org.hl7.elm.r1.Size) element); + } else if (element instanceof org.hl7.elm.r1.EndsWith) { + return map((org.hl7.elm.r1.EndsWith) element); + } else if (element instanceof org.hl7.elm.r1.As) { + return map((org.hl7.elm.r1.As) element); + } else if (element instanceof org.hl7.elm.r1.Xor) { + return map((org.hl7.elm.r1.Xor) element); + } else if (element instanceof org.hl7.elm.r1.Combine) { + return map((org.hl7.elm.r1.Combine) element); + } else if (element instanceof org.hl7.elm.r1.Starts) { + return map((org.hl7.elm.r1.Starts) element); + } else if (element instanceof org.hl7.elm.r1.TimeOfDay) { + return map((org.hl7.elm.r1.TimeOfDay) element); + } else if (element instanceof org.hl7.elm.r1.Power) { + return map((org.hl7.elm.r1.Power) element); + } else if (element instanceof org.hl7.elm.r1.AnyInValueSet) { + return map((org.hl7.elm.r1.AnyInValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Equal) { + return map((org.hl7.elm.r1.Equal) element); + } else if (element instanceof org.hl7.elm.r1.Date) { + return map((org.hl7.elm.r1.Date) element); + } else if (element instanceof org.hl7.elm.r1.Intersect) { + return map((org.hl7.elm.r1.Intersect) element); + } else if (element instanceof org.hl7.elm.r1.ToRatio) { + return map((org.hl7.elm.r1.ToRatio) element); + } else if (element instanceof org.hl7.elm.r1.Abs) { + return map((org.hl7.elm.r1.Abs) element); + } else if (element instanceof org.hl7.elm.r1.Subsumes) { + return map((org.hl7.elm.r1.Subsumes) element); + } else if (element instanceof org.hl7.elm.r1.ToList) { + return map((org.hl7.elm.r1.ToList) element); + } else if (element instanceof org.hl7.elm.r1.GreaterOrEqual) { + return map((org.hl7.elm.r1.GreaterOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.SubsumedBy) { + return map((org.hl7.elm.r1.SubsumedBy) element); + } else if (element instanceof org.hl7.elm.r1.Add) { + return map((org.hl7.elm.r1.Add) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAge) { + return map((org.hl7.elm.r1.CalculateAge) element); + } else if (element instanceof org.hl7.elm.r1.ToConcept) { + return map((org.hl7.elm.r1.ToConcept) element); + } else if (element instanceof org.hl7.elm.r1.Multiply) { + return map((org.hl7.elm.r1.Multiply) element); + } else if (element instanceof org.hl7.elm.r1.DateFrom) { + return map((org.hl7.elm.r1.DateFrom) element); + } else if (element instanceof org.hl7.elm.r1.SameOrAfter) { + return map((org.hl7.elm.r1.SameOrAfter) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToInteger) { + return map((org.hl7.elm.r1.ConvertsToInteger) element); + } else if (element instanceof org.hl7.elm.r1.ToLong) { + return map((org.hl7.elm.r1.ToLong) element); + } else if (element instanceof org.hl7.elm.r1.Coalesce) { + return map((org.hl7.elm.r1.Coalesce) element); + } else if (element instanceof org.hl7.elm.r1.Expand) { + return map((org.hl7.elm.r1.Expand) element); + } else if (element instanceof org.hl7.elm.r1.ProperIn) { + return map((org.hl7.elm.r1.ProperIn) element); + } else if (element instanceof org.hl7.elm.r1.StartsWith) { + return map((org.hl7.elm.r1.StartsWith) element); + } else if (element instanceof org.hl7.elm.r1.LowBoundary) { + return map((org.hl7.elm.r1.LowBoundary) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneFrom) { + return map((org.hl7.elm.r1.TimezoneFrom) element); + } else if (element instanceof org.hl7.elm.r1.Convert) { + return map((org.hl7.elm.r1.Convert) element); + } else if (element instanceof org.hl7.elm.r1.Less) { + return map((org.hl7.elm.r1.Less) element); + } else if (element instanceof org.hl7.elm.r1.End) { + return map((org.hl7.elm.r1.End) element); + } else if (element instanceof org.hl7.elm.r1.Now) { + return map((org.hl7.elm.r1.Now) element); + } else if (element instanceof org.hl7.elm.r1.Concatenate) { + return map((org.hl7.elm.r1.Concatenate) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDate) { + return map((org.hl7.elm.r1.ConvertsToDate) element); + } else if (element instanceof org.hl7.elm.r1.ConvertQuantity) { + return map((org.hl7.elm.r1.ConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Not) { + return map((org.hl7.elm.r1.Not) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDateTime) { + return map((org.hl7.elm.r1.ConvertsToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.Includes) { + return map((org.hl7.elm.r1.Includes) element); + } else if (element instanceof org.hl7.elm.r1.ToDecimal) { + return map((org.hl7.elm.r1.ToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.Start) { + return map((org.hl7.elm.r1.Start) element); + } else if (element instanceof org.hl7.elm.r1.IsFalse) { + return map((org.hl7.elm.r1.IsFalse) element); + } else if (element instanceof org.hl7.elm.r1.Modulo) { + return map((org.hl7.elm.r1.Modulo) element); + } else if (element instanceof org.hl7.elm.r1.Collapse) { + return map((org.hl7.elm.r1.Collapse) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDecimal) { + return map((org.hl7.elm.r1.ConvertsToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.IsNull) { + return map((org.hl7.elm.r1.IsNull) element); + } else if (element instanceof org.hl7.elm.r1.IsTrue) { + return map((org.hl7.elm.r1.IsTrue) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToRatio) { + return map((org.hl7.elm.r1.ConvertsToRatio) element); + } else if (element instanceof org.hl7.elm.r1.ReplaceMatches) { + return map((org.hl7.elm.r1.ReplaceMatches) element); + } else if (element instanceof org.hl7.elm.r1.MeetsAfter) { + return map((org.hl7.elm.r1.MeetsAfter) element); + } else if (element instanceof org.hl7.elm.r1.Overlaps) { + return map((org.hl7.elm.r1.Overlaps) element); + } else if (element instanceof org.hl7.elm.r1.Ceiling) { + return map((org.hl7.elm.r1.Ceiling) element); + } else if (element instanceof org.hl7.elm.r1.AnyInCodeSystem) { + return map((org.hl7.elm.r1.AnyInCodeSystem) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneOffsetFrom) { + return map((org.hl7.elm.r1.TimezoneOffsetFrom) element); + } else if (element instanceof org.hl7.elm.r1.Indexer) { + return map((org.hl7.elm.r1.Indexer) element); + } else if (element instanceof org.hl7.elm.r1.Message) { + return map((org.hl7.elm.r1.Message) element); + } else if (element instanceof org.hl7.elm.r1.Union) { + return map((org.hl7.elm.r1.Union) element); + } else if (element instanceof org.hl7.elm.r1.NotEqual) { + return map((org.hl7.elm.r1.NotEqual) element); + } else if (element instanceof org.hl7.elm.r1.Children) { + return map((org.hl7.elm.r1.Children) element); + } else if (element instanceof org.hl7.elm.r1.Ends) { + return map((org.hl7.elm.r1.Ends) element); + } else if (element instanceof org.hl7.elm.r1.DateTimeComponentFrom) { + return map((org.hl7.elm.r1.DateTimeComponentFrom) element); + } else if (element instanceof org.hl7.elm.r1.Except) { + return map((org.hl7.elm.r1.Except) element); + } else if (element instanceof org.hl7.elm.r1.TruncatedDivide) { + return map((org.hl7.elm.r1.TruncatedDivide) element); + } else if (element instanceof org.hl7.elm.r1.ToQuantity) { + return map((org.hl7.elm.r1.ToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Upper) { + return map((org.hl7.elm.r1.Upper) element); + } else if (element instanceof org.hl7.elm.r1.SameOrBefore) { + return map((org.hl7.elm.r1.SameOrBefore) element); + } else if (element instanceof org.hl7.elm.r1.In) { + return map((org.hl7.elm.r1.In) element); + } else if (element instanceof org.hl7.elm.r1.MeetsBefore) { + return map((org.hl7.elm.r1.MeetsBefore) element); + } else if (element instanceof org.hl7.elm.r1.Log) { + return map((org.hl7.elm.r1.Log) element); + } else if (element instanceof org.hl7.elm.r1.Is) { + return map((org.hl7.elm.r1.Is) element); + } else if (element instanceof org.hl7.elm.r1.ExpandValueSet) { + return map((org.hl7.elm.r1.ExpandValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Greater) { + return map((org.hl7.elm.r1.Greater) element); + } else if (element instanceof org.hl7.elm.r1.Truncate) { + return map((org.hl7.elm.r1.Truncate) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludes) { + return map((org.hl7.elm.r1.ProperIncludes) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToString) { + return map((org.hl7.elm.r1.ConvertsToString) element); + } else if (element instanceof org.hl7.elm.r1.Divide) { + return map((org.hl7.elm.r1.Divide) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsBefore) { + return map((org.hl7.elm.r1.OverlapsBefore) element); + } else if (element instanceof org.hl7.elm.r1.After) { + return map((org.hl7.elm.r1.After) element); + } else if (element instanceof org.hl7.elm.r1.ToTime) { + return map((org.hl7.elm.r1.ToTime) element); + } else if (element instanceof org.hl7.elm.r1.HighBoundary) { + return map((org.hl7.elm.r1.HighBoundary) element); + } else if (element instanceof org.hl7.elm.r1.Ln) { + return map((org.hl7.elm.r1.Ln) element); + } else if (element instanceof org.hl7.elm.r1.ToDate) { + return map((org.hl7.elm.r1.ToDate) element); + } else if (element instanceof org.hl7.elm.r1.TimeFrom) { + return map((org.hl7.elm.r1.TimeFrom) element); + } else if (element instanceof org.hl7.elm.r1.Subtract) { + return map((org.hl7.elm.r1.Subtract) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToTime) { + return map((org.hl7.elm.r1.ConvertsToTime) element); + } else if (element instanceof org.hl7.elm.r1.Predecessor) { + return map((org.hl7.elm.r1.Predecessor) element); + } else if (element instanceof org.hl7.elm.r1.Negate) { + return map((org.hl7.elm.r1.Negate) element); + } else if (element instanceof org.hl7.elm.r1.Before) { + return map((org.hl7.elm.r1.Before) element); + } else if (element instanceof org.hl7.elm.r1.Implies) { + return map((org.hl7.elm.r1.Implies) element); + } else if (element instanceof org.hl7.elm.r1.PointFrom) { + return map((org.hl7.elm.r1.PointFrom) element); + } else if (element instanceof org.hl7.elm.r1.Time) { + return map((org.hl7.elm.r1.Time) element); + } else if (element instanceof org.hl7.elm.r1.Distinct) { + return map((org.hl7.elm.r1.Distinct) element); + } else if (element instanceof org.hl7.elm.r1.DurationBetween) { + return map((org.hl7.elm.r1.DurationBetween) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToQuantity) { + return map((org.hl7.elm.r1.ConvertsToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Split) { + return map((org.hl7.elm.r1.Split) element); + } else if (element instanceof org.hl7.elm.r1.ToDateTime) { + return map((org.hl7.elm.r1.ToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.Today) { + return map((org.hl7.elm.r1.Today) element); + } else if (element instanceof org.hl7.elm.r1.Equivalent) { + return map((org.hl7.elm.r1.Equivalent) element); + } else if (element instanceof org.hl7.elm.r1.Round) { + return map((org.hl7.elm.r1.Round) element); + } else if (element instanceof org.hl7.elm.r1.DateTime) { + return map((org.hl7.elm.r1.DateTime) element); + } else if (element instanceof org.hl7.elm.r1.InValueSet) { + return map((org.hl7.elm.r1.InValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Floor) { + return map((org.hl7.elm.r1.Floor) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAgeAt) { + return map((org.hl7.elm.r1.CalculateAgeAt) element); + } else if (element instanceof org.hl7.elm.r1.LastPositionOf) { + return map((org.hl7.elm.r1.LastPositionOf) element); + } else if (element instanceof org.hl7.elm.r1.Last) { + return map((org.hl7.elm.r1.Last) element); + } else if (element instanceof org.hl7.elm.r1.Matches) { + return map((org.hl7.elm.r1.Matches) element); + } else if (element instanceof org.hl7.elm.r1.Times) { + return map((org.hl7.elm.r1.Times) element); + } else if (element instanceof org.hl7.elm.r1.Contains) { + return map((org.hl7.elm.r1.Contains) element); + } else if (element instanceof org.hl7.elm.r1.And) { + return map((org.hl7.elm.r1.And) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludedIn) { + return map((org.hl7.elm.r1.ProperIncludedIn) element); + } else if (element instanceof org.hl7.elm.r1.UnaryExpression) { + return map((org.hl7.elm.r1.UnaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.NaryExpression) { + return map((org.hl7.elm.r1.NaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.TernaryExpression) { + return map((org.hl7.elm.r1.TernaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.BinaryExpression) { + return map((org.hl7.elm.r1.BinaryExpression) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.OperatorExpression: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.TypeSpecifier map(org.hl7.elm.r1.TypeSpecifier element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.ParameterTypeSpecifier) { + return map((org.hl7.elm.r1.ParameterTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.IntervalTypeSpecifier) { + return map((org.hl7.elm.r1.IntervalTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.ListTypeSpecifier) { + return map((org.hl7.elm.r1.ListTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.ChoiceTypeSpecifier) { + return map((org.hl7.elm.r1.ChoiceTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.NamedTypeSpecifier) { + return map((org.hl7.elm.r1.NamedTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.TupleTypeSpecifier) { + return map((org.hl7.elm.r1.TupleTypeSpecifier) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.TypeSpecifier: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.TernaryExpression map(org.hl7.elm.r1.TernaryExpression element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.ReplaceMatches) { + return map((org.hl7.elm.r1.ReplaceMatches) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.TernaryExpression: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.BinaryExpression map(org.hl7.elm.r1.BinaryExpression element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.CanConvertQuantity) { + return map((org.hl7.elm.r1.CanConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Meets) { + return map((org.hl7.elm.r1.Meets) element); + } else if (element instanceof org.hl7.elm.r1.DifferenceBetween) { + return map((org.hl7.elm.r1.DifferenceBetween) element); + } else if (element instanceof org.hl7.elm.r1.IncludedIn) { + return map((org.hl7.elm.r1.IncludedIn) element); + } else if (element instanceof org.hl7.elm.r1.Or) { + return map((org.hl7.elm.r1.Or) element); + } else if (element instanceof org.hl7.elm.r1.LessOrEqual) { + return map((org.hl7.elm.r1.LessOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsAfter) { + return map((org.hl7.elm.r1.OverlapsAfter) element); + } else if (element instanceof org.hl7.elm.r1.ProperContains) { + return map((org.hl7.elm.r1.ProperContains) element); + } else if (element instanceof org.hl7.elm.r1.SameAs) { + return map((org.hl7.elm.r1.SameAs) element); + } else if (element instanceof org.hl7.elm.r1.EndsWith) { + return map((org.hl7.elm.r1.EndsWith) element); + } else if (element instanceof org.hl7.elm.r1.Xor) { + return map((org.hl7.elm.r1.Xor) element); + } else if (element instanceof org.hl7.elm.r1.Starts) { + return map((org.hl7.elm.r1.Starts) element); + } else if (element instanceof org.hl7.elm.r1.Power) { + return map((org.hl7.elm.r1.Power) element); + } else if (element instanceof org.hl7.elm.r1.Equal) { + return map((org.hl7.elm.r1.Equal) element); + } else if (element instanceof org.hl7.elm.r1.Subsumes) { + return map((org.hl7.elm.r1.Subsumes) element); + } else if (element instanceof org.hl7.elm.r1.GreaterOrEqual) { + return map((org.hl7.elm.r1.GreaterOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.SubsumedBy) { + return map((org.hl7.elm.r1.SubsumedBy) element); + } else if (element instanceof org.hl7.elm.r1.Add) { + return map((org.hl7.elm.r1.Add) element); + } else if (element instanceof org.hl7.elm.r1.Multiply) { + return map((org.hl7.elm.r1.Multiply) element); + } else if (element instanceof org.hl7.elm.r1.SameOrAfter) { + return map((org.hl7.elm.r1.SameOrAfter) element); + } else if (element instanceof org.hl7.elm.r1.Expand) { + return map((org.hl7.elm.r1.Expand) element); + } else if (element instanceof org.hl7.elm.r1.ProperIn) { + return map((org.hl7.elm.r1.ProperIn) element); + } else if (element instanceof org.hl7.elm.r1.StartsWith) { + return map((org.hl7.elm.r1.StartsWith) element); + } else if (element instanceof org.hl7.elm.r1.LowBoundary) { + return map((org.hl7.elm.r1.LowBoundary) element); + } else if (element instanceof org.hl7.elm.r1.Less) { + return map((org.hl7.elm.r1.Less) element); + } else if (element instanceof org.hl7.elm.r1.ConvertQuantity) { + return map((org.hl7.elm.r1.ConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Includes) { + return map((org.hl7.elm.r1.Includes) element); + } else if (element instanceof org.hl7.elm.r1.Modulo) { + return map((org.hl7.elm.r1.Modulo) element); + } else if (element instanceof org.hl7.elm.r1.Collapse) { + return map((org.hl7.elm.r1.Collapse) element); + } else if (element instanceof org.hl7.elm.r1.MeetsAfter) { + return map((org.hl7.elm.r1.MeetsAfter) element); + } else if (element instanceof org.hl7.elm.r1.Overlaps) { + return map((org.hl7.elm.r1.Overlaps) element); + } else if (element instanceof org.hl7.elm.r1.Indexer) { + return map((org.hl7.elm.r1.Indexer) element); + } else if (element instanceof org.hl7.elm.r1.NotEqual) { + return map((org.hl7.elm.r1.NotEqual) element); + } else if (element instanceof org.hl7.elm.r1.Ends) { + return map((org.hl7.elm.r1.Ends) element); + } else if (element instanceof org.hl7.elm.r1.TruncatedDivide) { + return map((org.hl7.elm.r1.TruncatedDivide) element); + } else if (element instanceof org.hl7.elm.r1.SameOrBefore) { + return map((org.hl7.elm.r1.SameOrBefore) element); + } else if (element instanceof org.hl7.elm.r1.In) { + return map((org.hl7.elm.r1.In) element); + } else if (element instanceof org.hl7.elm.r1.MeetsBefore) { + return map((org.hl7.elm.r1.MeetsBefore) element); + } else if (element instanceof org.hl7.elm.r1.Log) { + return map((org.hl7.elm.r1.Log) element); + } else if (element instanceof org.hl7.elm.r1.Greater) { + return map((org.hl7.elm.r1.Greater) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludes) { + return map((org.hl7.elm.r1.ProperIncludes) element); + } else if (element instanceof org.hl7.elm.r1.Divide) { + return map((org.hl7.elm.r1.Divide) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsBefore) { + return map((org.hl7.elm.r1.OverlapsBefore) element); + } else if (element instanceof org.hl7.elm.r1.After) { + return map((org.hl7.elm.r1.After) element); + } else if (element instanceof org.hl7.elm.r1.HighBoundary) { + return map((org.hl7.elm.r1.HighBoundary) element); + } else if (element instanceof org.hl7.elm.r1.Subtract) { + return map((org.hl7.elm.r1.Subtract) element); + } else if (element instanceof org.hl7.elm.r1.Before) { + return map((org.hl7.elm.r1.Before) element); + } else if (element instanceof org.hl7.elm.r1.Implies) { + return map((org.hl7.elm.r1.Implies) element); + } else if (element instanceof org.hl7.elm.r1.DurationBetween) { + return map((org.hl7.elm.r1.DurationBetween) element); + } else if (element instanceof org.hl7.elm.r1.Equivalent) { + return map((org.hl7.elm.r1.Equivalent) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAgeAt) { + return map((org.hl7.elm.r1.CalculateAgeAt) element); + } else if (element instanceof org.hl7.elm.r1.Matches) { + return map((org.hl7.elm.r1.Matches) element); + } else if (element instanceof org.hl7.elm.r1.Times) { + return map((org.hl7.elm.r1.Times) element); + } else if (element instanceof org.hl7.elm.r1.Contains) { + return map((org.hl7.elm.r1.Contains) element); + } else if (element instanceof org.hl7.elm.r1.And) { + return map((org.hl7.elm.r1.And) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludedIn) { + return map((org.hl7.elm.r1.ProperIncludedIn) element); + } + + throw new IllegalArgumentException( + "unknown class of org.hl7.elm.r1.BinaryExpression: " + element.getClass().getName()); + } + + default org.cqframework.cql.elm.execution.Element map(org.hl7.elm.r1.Element element) { + if (element == null) { + return null; + } + + if (element instanceof org.hl7.elm.r1.First) { + return map((org.hl7.elm.r1.First) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToBoolean) { + return map((org.hl7.elm.r1.ConvertsToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.CanConvertQuantity) { + return map((org.hl7.elm.r1.CanConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Meets) { + return map((org.hl7.elm.r1.Meets) element); + } else if (element instanceof org.hl7.elm.r1.ToString) { + return map((org.hl7.elm.r1.ToString) element); + } else if (element instanceof org.hl7.elm.r1.DateFilterElement) { + return map((org.hl7.elm.r1.DateFilterElement) element); + } else if (element instanceof org.hl7.elm.r1.MinValue) { + return map((org.hl7.elm.r1.MinValue) element); + } else if (element instanceof org.hl7.elm.r1.DifferenceBetween) { + return map((org.hl7.elm.r1.DifferenceBetween) element); + } else if (element instanceof org.hl7.elm.r1.Exists) { + return map((org.hl7.elm.r1.Exists) element); + } else if (element instanceof org.hl7.elm.r1.CanConvert) { + return map((org.hl7.elm.r1.CanConvert) element); + } else if (element instanceof org.hl7.elm.r1.ReturnClause) { + return map((org.hl7.elm.r1.ReturnClause) element); + } else if (element instanceof org.hl7.elm.r1.IncludedIn) { + return map((org.hl7.elm.r1.IncludedIn) element); + } else if (element instanceof org.hl7.elm.r1.Or) { + return map((org.hl7.elm.r1.Or) element); + } else if (element instanceof org.hl7.elm.r1.Current) { + return map((org.hl7.elm.r1.Current) element); + } else if (element instanceof org.hl7.elm.r1.Substring) { + return map((org.hl7.elm.r1.Substring) element); + } else if (element instanceof org.hl7.elm.r1.ToBoolean) { + return map((org.hl7.elm.r1.ToBoolean) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToLong) { + return map((org.hl7.elm.r1.ConvertsToLong) element); + } else if (element instanceof org.hl7.elm.r1.Slice) { + return map((org.hl7.elm.r1.Slice) element); + } else if (element instanceof org.hl7.elm.r1.LessOrEqual) { + return map((org.hl7.elm.r1.LessOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.Exp) { + return map((org.hl7.elm.r1.Exp) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsAfter) { + return map((org.hl7.elm.r1.OverlapsAfter) element); + } else if (element instanceof org.hl7.elm.r1.Repeat) { + return map((org.hl7.elm.r1.Repeat) element); + } else if (element instanceof org.hl7.elm.r1.CodeSystemRef) { + return map((org.hl7.elm.r1.CodeSystemRef) element); + } else if (element instanceof org.hl7.elm.r1.Flatten) { + return map((org.hl7.elm.r1.Flatten) element); + } else if (element instanceof org.hl7.elm.r1.Lower) { + return map((org.hl7.elm.r1.Lower) element); + } else if (element instanceof org.hl7.elm.r1.Precision) { + return map((org.hl7.elm.r1.Precision) element); + } else if (element instanceof org.hl7.elm.r1.IndexOf) { + return map((org.hl7.elm.r1.IndexOf) element); + } else if (element instanceof org.hl7.elm.r1.ProperContains) { + return map((org.hl7.elm.r1.ProperContains) element); + } else if (element instanceof org.hl7.elm.r1.Length) { + return map((org.hl7.elm.r1.Length) element); + } else if (element instanceof org.hl7.elm.r1.OperandDef) { + return map((org.hl7.elm.r1.OperandDef) element); + } else if (element instanceof org.hl7.elm.r1.FunctionDef) { + return map((org.hl7.elm.r1.FunctionDef) element); + } else if (element instanceof org.hl7.elm.r1.PositionOf) { + return map((org.hl7.elm.r1.PositionOf) element); + } else if (element instanceof org.hl7.elm.r1.Code) { + return map((org.hl7.elm.r1.Code) element); + } else if (element instanceof org.hl7.elm.r1.UsingDef) { + return map((org.hl7.elm.r1.UsingDef) element); + } else if (element instanceof org.hl7.elm.r1.Successor) { + return map((org.hl7.elm.r1.Successor) element); + } else if (element instanceof org.hl7.elm.r1.SplitOnMatches) { + return map((org.hl7.elm.r1.SplitOnMatches) element); + } else if (element instanceof org.hl7.elm.r1.Retrieve) { + return map((org.hl7.elm.r1.Retrieve) element); + } else if (element instanceof org.hl7.elm.r1.CodeDef) { + return map((org.hl7.elm.r1.CodeDef) element); + } else if (element instanceof org.hl7.elm.r1.Width) { + return map((org.hl7.elm.r1.Width) element); + } else if (element instanceof org.hl7.elm.r1.ToInteger) { + return map((org.hl7.elm.r1.ToInteger) element); + } else if (element instanceof org.hl7.elm.r1.ParameterDef) { + return map((org.hl7.elm.r1.ParameterDef) element); + } else if (element instanceof org.hl7.elm.r1.SameAs) { + return map((org.hl7.elm.r1.SameAs) element); + } else if (element instanceof org.hl7.elm.r1.Descendents) { + return map((org.hl7.elm.r1.Descendents) element); + } else if (element instanceof org.hl7.elm.r1.ToChars) { + return map((org.hl7.elm.r1.ToChars) element); + } else if (element instanceof org.hl7.elm.r1.SingletonFrom) { + return map((org.hl7.elm.r1.SingletonFrom) element); + } else if (element instanceof org.hl7.elm.r1.SortClause) { + return map((org.hl7.elm.r1.SortClause) element); + } else if (element instanceof org.hl7.elm.r1.InCodeSystem) { + return map((org.hl7.elm.r1.InCodeSystem) element); + } else if (element instanceof org.hl7.elm.r1.Sum) { + return map((org.hl7.elm.r1.Sum) element); + } else if (element instanceof org.hl7.elm.r1.Size) { + return map((org.hl7.elm.r1.Size) element); + } else if (element instanceof org.hl7.elm.r1.Interval) { + return map((org.hl7.elm.r1.Interval) element); + } else if (element instanceof org.hl7.elm.r1.EndsWith) { + return map((org.hl7.elm.r1.EndsWith) element); + } else if (element instanceof org.hl7.elm.r1.Tuple) { + return map((org.hl7.elm.r1.Tuple) element); + } else if (element instanceof org.hl7.elm.r1.As) { + return map((org.hl7.elm.r1.As) element); + } else if (element instanceof org.hl7.elm.r1.ValueSetRef) { + return map((org.hl7.elm.r1.ValueSetRef) element); + } else if (element instanceof org.hl7.elm.r1.Xor) { + return map((org.hl7.elm.r1.Xor) element); + } else if (element instanceof org.hl7.elm.r1.Combine) { + return map((org.hl7.elm.r1.Combine) element); + } else if (element instanceof org.hl7.elm.r1.Starts) { + return map((org.hl7.elm.r1.Starts) element); + } else if (element instanceof org.hl7.elm.r1.TimeOfDay) { + return map((org.hl7.elm.r1.TimeOfDay) element); + } else if (element instanceof org.hl7.elm.r1.Min) { + return map((org.hl7.elm.r1.Min) element); + } else if (element instanceof org.hl7.elm.r1.AggregateClause) { + return map((org.hl7.elm.r1.AggregateClause) element); + } else if (element instanceof org.hl7.elm.r1.Power) { + return map((org.hl7.elm.r1.Power) element); + } else if (element instanceof org.hl7.elm.r1.ValueSetDef) { + return map((org.hl7.elm.r1.ValueSetDef) element); + } else if (element instanceof org.hl7.elm.r1.AnyInValueSet) { + return map((org.hl7.elm.r1.AnyInValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Count) { + return map((org.hl7.elm.r1.Count) element); + } else if (element instanceof org.hl7.elm.r1.ExpressionRef) { + return map((org.hl7.elm.r1.ExpressionRef) element); + } else if (element instanceof org.hl7.elm.r1.Query) { + return map((org.hl7.elm.r1.Query) element); + } else if (element instanceof org.hl7.elm.r1.Sort) { + return map((org.hl7.elm.r1.Sort) element); + } else if (element instanceof org.hl7.elm.r1.Mode) { + return map((org.hl7.elm.r1.Mode) element); + } else if (element instanceof org.hl7.elm.r1.Equal) { + return map((org.hl7.elm.r1.Equal) element); + } else if (element instanceof org.hl7.elm.r1.TupleElementDefinition) { + return map((org.hl7.elm.r1.TupleElementDefinition) element); + } else if (element instanceof org.hl7.elm.r1.Date) { + return map((org.hl7.elm.r1.Date) element); + } else if (element instanceof org.hl7.elm.r1.ForEach) { + return map((org.hl7.elm.r1.ForEach) element); + } else if (element instanceof org.hl7.elm.r1.Intersect) { + return map((org.hl7.elm.r1.Intersect) element); + } else if (element instanceof org.hl7.elm.r1.OtherFilterElement) { + return map((org.hl7.elm.r1.OtherFilterElement) element); + } else if (element instanceof org.hl7.elm.r1.ToRatio) { + return map((org.hl7.elm.r1.ToRatio) element); + } else if (element instanceof org.hl7.elm.r1.Abs) { + return map((org.hl7.elm.r1.Abs) element); + } else if (element instanceof org.hl7.elm.r1.ParameterTypeSpecifier) { + return map((org.hl7.elm.r1.ParameterTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.ConceptRef) { + return map((org.hl7.elm.r1.ConceptRef) element); + } else if (element instanceof org.hl7.elm.r1.IdentifierRef) { + return map((org.hl7.elm.r1.IdentifierRef) element); + } else if (element instanceof org.hl7.elm.r1.Subsumes) { + return map((org.hl7.elm.r1.Subsumes) element); + } else if (element instanceof org.hl7.elm.r1.ToList) { + return map((org.hl7.elm.r1.ToList) element); + } else if (element instanceof org.hl7.elm.r1.LetClause) { + return map((org.hl7.elm.r1.LetClause) element); + } else if (element instanceof org.hl7.elm.r1.ParameterRef) { + return map((org.hl7.elm.r1.ParameterRef) element); + } else if (element instanceof org.hl7.elm.r1.GreaterOrEqual) { + return map((org.hl7.elm.r1.GreaterOrEqual) element); + } else if (element instanceof org.hl7.elm.r1.SubsumedBy) { + return map((org.hl7.elm.r1.SubsumedBy) element); + } else if (element instanceof org.hl7.elm.r1.Add) { + return map((org.hl7.elm.r1.Add) element); + } else if (element instanceof org.hl7.elm.r1.Library) { + return map((org.hl7.elm.r1.Library) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAge) { + return map((org.hl7.elm.r1.CalculateAge) element); + } else if (element instanceof org.hl7.elm.r1.ContextDef) { + return map((org.hl7.elm.r1.ContextDef) element); + } else if (element instanceof org.hl7.elm.r1.IntervalTypeSpecifier) { + return map((org.hl7.elm.r1.IntervalTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.ToConcept) { + return map((org.hl7.elm.r1.ToConcept) element); + } else if (element instanceof org.hl7.elm.r1.ListTypeSpecifier) { + return map((org.hl7.elm.r1.ListTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.Multiply) { + return map((org.hl7.elm.r1.Multiply) element); + } else if (element instanceof org.hl7.elm.r1.DateFrom) { + return map((org.hl7.elm.r1.DateFrom) element); + } else if (element instanceof org.hl7.elm.r1.SameOrAfter) { + return map((org.hl7.elm.r1.SameOrAfter) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToInteger) { + return map((org.hl7.elm.r1.ConvertsToInteger) element); + } else if (element instanceof org.hl7.elm.r1.ToLong) { + return map((org.hl7.elm.r1.ToLong) element); + } else if (element instanceof org.hl7.elm.r1.AllTrue) { + return map((org.hl7.elm.r1.AllTrue) element); + } else if (element instanceof org.hl7.elm.r1.ByDirection) { + return map((org.hl7.elm.r1.ByDirection) element); + } else if (element instanceof org.hl7.elm.r1.Coalesce) { + return map((org.hl7.elm.r1.Coalesce) element); + } else if (element instanceof org.hl7.elm.r1.FunctionRef) { + return map((org.hl7.elm.r1.FunctionRef) element); + } else if (element instanceof org.hl7.elm.r1.Expand) { + return map((org.hl7.elm.r1.Expand) element); + } else if (element instanceof org.hl7.elm.r1.PopulationVariance) { + return map((org.hl7.elm.r1.PopulationVariance) element); + } else if (element instanceof org.hl7.elm.r1.Filter) { + return map((org.hl7.elm.r1.Filter) element); + } else if (element instanceof org.hl7.elm.r1.ProperIn) { + return map((org.hl7.elm.r1.ProperIn) element); + } else if (element instanceof org.hl7.elm.r1.StartsWith) { + return map((org.hl7.elm.r1.StartsWith) element); + } else if (element instanceof org.hl7.elm.r1.IncludeElement) { + return map((org.hl7.elm.r1.IncludeElement) element); + } else if (element instanceof org.hl7.elm.r1.Null) { + return map((org.hl7.elm.r1.Null) element); + } else if (element instanceof org.hl7.elm.r1.LowBoundary) { + return map((org.hl7.elm.r1.LowBoundary) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneFrom) { + return map((org.hl7.elm.r1.TimezoneFrom) element); + } else if (element instanceof org.hl7.elm.r1.Convert) { + return map((org.hl7.elm.r1.Convert) element); + } else if (element instanceof org.hl7.elm.r1.Less) { + return map((org.hl7.elm.r1.Less) element); + } else if (element instanceof org.hl7.elm.r1.Search) { + return map((org.hl7.elm.r1.Search) element); + } else if (element instanceof org.hl7.elm.r1.End) { + return map((org.hl7.elm.r1.End) element); + } else if (element instanceof org.hl7.elm.r1.QueryLetRef) { + return map((org.hl7.elm.r1.QueryLetRef) element); + } else if (element instanceof org.hl7.elm.r1.Now) { + return map((org.hl7.elm.r1.Now) element); + } else if (element instanceof org.hl7.elm.r1.Concatenate) { + return map((org.hl7.elm.r1.Concatenate) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDate) { + return map((org.hl7.elm.r1.ConvertsToDate) element); + } else if (element instanceof org.hl7.elm.r1.ConvertQuantity) { + return map((org.hl7.elm.r1.ConvertQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Avg) { + return map((org.hl7.elm.r1.Avg) element); + } else if (element instanceof org.hl7.elm.r1.Not) { + return map((org.hl7.elm.r1.Not) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDateTime) { + return map((org.hl7.elm.r1.ConvertsToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.Includes) { + return map((org.hl7.elm.r1.Includes) element); + } else if (element instanceof org.hl7.elm.r1.ToDecimal) { + return map((org.hl7.elm.r1.ToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.Start) { + return map((org.hl7.elm.r1.Start) element); + } else if (element instanceof org.hl7.elm.r1.IsFalse) { + return map((org.hl7.elm.r1.IsFalse) element); + } else if (element instanceof org.hl7.elm.r1.Modulo) { + return map((org.hl7.elm.r1.Modulo) element); + } else if (element instanceof org.hl7.elm.r1.Collapse) { + return map((org.hl7.elm.r1.Collapse) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToDecimal) { + return map((org.hl7.elm.r1.ConvertsToDecimal) element); + } else if (element instanceof org.hl7.elm.r1.Case) { + return map((org.hl7.elm.r1.Case) element); + } else if (element instanceof org.hl7.elm.r1.ConceptDef) { + return map((org.hl7.elm.r1.ConceptDef) element); + } else if (element instanceof org.hl7.elm.r1.Instance) { + return map((org.hl7.elm.r1.Instance) element); + } else if (element instanceof org.hl7.elm.r1.IsNull) { + return map((org.hl7.elm.r1.IsNull) element); + } else if (element instanceof org.hl7.elm.r1.StdDev) { + return map((org.hl7.elm.r1.StdDev) element); + } else if (element instanceof org.hl7.elm.r1.IsTrue) { + return map((org.hl7.elm.r1.IsTrue) element); + } else if (element instanceof org.hl7.elm.r1.If) { + return map((org.hl7.elm.r1.If) element); + } else if (element instanceof org.hl7.elm.r1.Product) { + return map((org.hl7.elm.r1.Product) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToRatio) { + return map((org.hl7.elm.r1.ConvertsToRatio) element); + } else if (element instanceof org.hl7.elm.r1.CodeRef) { + return map((org.hl7.elm.r1.CodeRef) element); + } else if (element instanceof org.hl7.elm.r1.ReplaceMatches) { + return map((org.hl7.elm.r1.ReplaceMatches) element); + } else if (element instanceof org.hl7.elm.r1.ByExpression) { + return map((org.hl7.elm.r1.ByExpression) element); + } else if (element instanceof org.hl7.elm.r1.MeetsAfter) { + return map((org.hl7.elm.r1.MeetsAfter) element); + } else if (element instanceof org.hl7.elm.r1.Overlaps) { + return map((org.hl7.elm.r1.Overlaps) element); + } else if (element instanceof org.hl7.elm.r1.Ceiling) { + return map((org.hl7.elm.r1.Ceiling) element); + } else if (element instanceof org.hl7.elm.r1.AnyInCodeSystem) { + return map((org.hl7.elm.r1.AnyInCodeSystem) element); + } else if (element instanceof org.hl7.elm.r1.TimezoneOffsetFrom) { + return map((org.hl7.elm.r1.TimezoneOffsetFrom) element); + } else if (element instanceof org.hl7.elm.r1.Indexer) { + return map((org.hl7.elm.r1.Indexer) element); + } else if (element instanceof org.hl7.elm.r1.Message) { + return map((org.hl7.elm.r1.Message) element); + } else if (element instanceof org.hl7.elm.r1.CaseItem) { + return map((org.hl7.elm.r1.CaseItem) element); + } else if (element instanceof org.hl7.elm.r1.Union) { + return map((org.hl7.elm.r1.Union) element); + } else if (element instanceof org.hl7.elm.r1.NotEqual) { + return map((org.hl7.elm.r1.NotEqual) element); + } else if (element instanceof org.hl7.elm.r1.Children) { + return map((org.hl7.elm.r1.Children) element); + } else if (element instanceof org.hl7.elm.r1.Ends) { + return map((org.hl7.elm.r1.Ends) element); + } else if (element instanceof org.hl7.elm.r1.DateTimeComponentFrom) { + return map((org.hl7.elm.r1.DateTimeComponentFrom) element); + } else if (element instanceof org.hl7.elm.r1.Iteration) { + return map((org.hl7.elm.r1.Iteration) element); + } else if (element instanceof org.hl7.elm.r1.IncludeDef) { + return map((org.hl7.elm.r1.IncludeDef) element); + } else if (element instanceof org.hl7.elm.r1.Except) { + return map((org.hl7.elm.r1.Except) element); + } else if (element instanceof org.hl7.elm.r1.Ratio) { + return map((org.hl7.elm.r1.Ratio) element); + } else if (element instanceof org.hl7.elm.r1.Quantity) { + return map((org.hl7.elm.r1.Quantity) element); + } else if (element instanceof org.hl7.elm.r1.TruncatedDivide) { + return map((org.hl7.elm.r1.TruncatedDivide) element); + } else if (element instanceof org.hl7.elm.r1.ToQuantity) { + return map((org.hl7.elm.r1.ToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Upper) { + return map((org.hl7.elm.r1.Upper) element); + } else if (element instanceof org.hl7.elm.r1.SameOrBefore) { + return map((org.hl7.elm.r1.SameOrBefore) element); + } else if (element instanceof org.hl7.elm.r1.In) { + return map((org.hl7.elm.r1.In) element); + } else if (element instanceof org.hl7.elm.r1.MeetsBefore) { + return map((org.hl7.elm.r1.MeetsBefore) element); + } else if (element instanceof org.hl7.elm.r1.Log) { + return map((org.hl7.elm.r1.Log) element); + } else if (element instanceof org.hl7.elm.r1.Is) { + return map((org.hl7.elm.r1.Is) element); + } else if (element instanceof org.hl7.elm.r1.ExpandValueSet) { + return map((org.hl7.elm.r1.ExpandValueSet) element); + } else if (element instanceof org.hl7.elm.r1.Greater) { + return map((org.hl7.elm.r1.Greater) element); + } else if (element instanceof org.hl7.elm.r1.Truncate) { + return map((org.hl7.elm.r1.Truncate) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludes) { + return map((org.hl7.elm.r1.ProperIncludes) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToString) { + return map((org.hl7.elm.r1.ConvertsToString) element); + } else if (element instanceof org.hl7.elm.r1.Divide) { + return map((org.hl7.elm.r1.Divide) element); + } else if (element instanceof org.hl7.elm.r1.OverlapsBefore) { + return map((org.hl7.elm.r1.OverlapsBefore) element); + } else if (element instanceof org.hl7.elm.r1.After) { + return map((org.hl7.elm.r1.After) element); + } else if (element instanceof org.hl7.elm.r1.ToTime) { + return map((org.hl7.elm.r1.ToTime) element); + } else if (element instanceof org.hl7.elm.r1.ChoiceTypeSpecifier) { + return map((org.hl7.elm.r1.ChoiceTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.HighBoundary) { + return map((org.hl7.elm.r1.HighBoundary) element); + } else if (element instanceof org.hl7.elm.r1.Ln) { + return map((org.hl7.elm.r1.Ln) element); + } else if (element instanceof org.hl7.elm.r1.ToDate) { + return map((org.hl7.elm.r1.ToDate) element); + } else if (element instanceof org.hl7.elm.r1.TimeFrom) { + return map((org.hl7.elm.r1.TimeFrom) element); + } else if (element instanceof org.hl7.elm.r1.Subtract) { + return map((org.hl7.elm.r1.Subtract) element); + } else if (element instanceof org.hl7.elm.r1.List) { + return map((org.hl7.elm.r1.List) element); + } else if (element instanceof org.hl7.elm.r1.Max) { + return map((org.hl7.elm.r1.Max) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToTime) { + return map((org.hl7.elm.r1.ConvertsToTime) element); + } else if (element instanceof org.hl7.elm.r1.Predecessor) { + return map((org.hl7.elm.r1.Predecessor) element); + } else if (element instanceof org.hl7.elm.r1.CodeFilterElement) { + return map((org.hl7.elm.r1.CodeFilterElement) element); + } else if (element instanceof org.hl7.elm.r1.Negate) { + return map((org.hl7.elm.r1.Negate) element); + } else if (element instanceof org.hl7.elm.r1.Before) { + return map((org.hl7.elm.r1.Before) element); + } else if (element instanceof org.hl7.elm.r1.Implies) { + return map((org.hl7.elm.r1.Implies) element); + } else if (element instanceof org.hl7.elm.r1.PointFrom) { + return map((org.hl7.elm.r1.PointFrom) element); + } else if (element instanceof org.hl7.elm.r1.ByColumn) { + return map((org.hl7.elm.r1.ByColumn) element); + } else if (element instanceof org.hl7.elm.r1.Variance) { + return map((org.hl7.elm.r1.Variance) element); + } else if (element instanceof org.hl7.elm.r1.Time) { + return map((org.hl7.elm.r1.Time) element); + } else if (element instanceof org.hl7.elm.r1.Property) { + return map((org.hl7.elm.r1.Property) element); + } else if (element instanceof org.hl7.elm.r1.Distinct) { + return map((org.hl7.elm.r1.Distinct) element); + } else if (element instanceof org.hl7.elm.r1.DurationBetween) { + return map((org.hl7.elm.r1.DurationBetween) element); + } else if (element instanceof org.hl7.elm.r1.Literal) { + return map((org.hl7.elm.r1.Literal) element); + } else if (element instanceof org.hl7.elm.r1.CodeSystemDef) { + return map((org.hl7.elm.r1.CodeSystemDef) element); + } else if (element instanceof org.hl7.elm.r1.Without) { + return map((org.hl7.elm.r1.Without) element); + } else if (element instanceof org.hl7.elm.r1.ConvertsToQuantity) { + return map((org.hl7.elm.r1.ConvertsToQuantity) element); + } else if (element instanceof org.hl7.elm.r1.Concept) { + return map((org.hl7.elm.r1.Concept) element); + } else if (element instanceof org.hl7.elm.r1.With) { + return map((org.hl7.elm.r1.With) element); + } else if (element instanceof org.hl7.elm.r1.GeometricMean) { + return map((org.hl7.elm.r1.GeometricMean) element); + } else if (element instanceof org.hl7.elm.r1.Split) { + return map((org.hl7.elm.r1.Split) element); + } else if (element instanceof org.hl7.elm.r1.Aggregate) { + return map((org.hl7.elm.r1.Aggregate) element); + } else if (element instanceof org.hl7.elm.r1.ToDateTime) { + return map((org.hl7.elm.r1.ToDateTime) element); + } else if (element instanceof org.hl7.elm.r1.Today) { + return map((org.hl7.elm.r1.Today) element); + } else if (element instanceof org.hl7.elm.r1.Median) { + return map((org.hl7.elm.r1.Median) element); + } else if (element instanceof org.hl7.elm.r1.Equivalent) { + return map((org.hl7.elm.r1.Equivalent) element); + } else if (element instanceof org.hl7.elm.r1.Round) { + return map((org.hl7.elm.r1.Round) element); + } else if (element instanceof org.hl7.elm.r1.DateTime) { + return map((org.hl7.elm.r1.DateTime) element); + } else if (element instanceof org.hl7.elm.r1.MaxValue) { + return map((org.hl7.elm.r1.MaxValue) element); + } else if (element instanceof org.hl7.elm.r1.InValueSet) { + return map((org.hl7.elm.r1.InValueSet) element); + } else if (element instanceof org.hl7.elm.r1.NamedTypeSpecifier) { + return map((org.hl7.elm.r1.NamedTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.Floor) { + return map((org.hl7.elm.r1.Floor) element); + } else if (element instanceof org.hl7.elm.r1.PopulationStdDev) { + return map((org.hl7.elm.r1.PopulationStdDev) element); + } else if (element instanceof org.hl7.elm.r1.TupleTypeSpecifier) { + return map((org.hl7.elm.r1.TupleTypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.CalculateAgeAt) { + return map((org.hl7.elm.r1.CalculateAgeAt) element); + } else if (element instanceof org.hl7.elm.r1.AliasRef) { + return map((org.hl7.elm.r1.AliasRef) element); + } else if (element instanceof org.hl7.elm.r1.LastPositionOf) { + return map((org.hl7.elm.r1.LastPositionOf) element); + } else if (element instanceof org.hl7.elm.r1.Last) { + return map((org.hl7.elm.r1.Last) element); + } else if (element instanceof org.hl7.elm.r1.Matches) { + return map((org.hl7.elm.r1.Matches) element); + } else if (element instanceof org.hl7.elm.r1.Times) { + return map((org.hl7.elm.r1.Times) element); + } else if (element instanceof org.hl7.elm.r1.Total) { + return map((org.hl7.elm.r1.Total) element); + } else if (element instanceof org.hl7.elm.r1.Contains) { + return map((org.hl7.elm.r1.Contains) element); + } else if (element instanceof org.hl7.elm.r1.OperandRef) { + return map((org.hl7.elm.r1.OperandRef) element); + } else if (element instanceof org.hl7.elm.r1.And) { + return map((org.hl7.elm.r1.And) element); + } else if (element instanceof org.hl7.elm.r1.AnyTrue) { + return map((org.hl7.elm.r1.AnyTrue) element); + } else if (element instanceof org.hl7.elm.r1.ProperIncludedIn) { + return map((org.hl7.elm.r1.ProperIncludedIn) element); + } else if (element instanceof org.hl7.elm.r1.ExpressionDef) { + return map((org.hl7.elm.r1.ExpressionDef) element); + } else if (element instanceof org.hl7.elm.r1.AliasedQuerySource) { + return map((org.hl7.elm.r1.AliasedQuerySource) element); + } else if (element instanceof org.hl7.elm.r1.SortByItem) { + return map((org.hl7.elm.r1.SortByItem) element); + } else if (element instanceof org.hl7.elm.r1.AggregateExpression) { + return map((org.hl7.elm.r1.AggregateExpression) element); + } else if (element instanceof org.hl7.elm.r1.UnaryExpression) { + return map((org.hl7.elm.r1.UnaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.RelationshipClause) { + return map((org.hl7.elm.r1.RelationshipClause) element); + } else if (element instanceof org.hl7.elm.r1.Expression) { + return map((org.hl7.elm.r1.Expression) element); + } else if (element instanceof org.hl7.elm.r1.NaryExpression) { + return map((org.hl7.elm.r1.NaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.OperatorExpression) { + return map((org.hl7.elm.r1.OperatorExpression) element); + } else if (element instanceof org.hl7.elm.r1.TypeSpecifier) { + return map((org.hl7.elm.r1.TypeSpecifier) element); + } else if (element instanceof org.hl7.elm.r1.TernaryExpression) { + return map((org.hl7.elm.r1.TernaryExpression) element); + } else if (element instanceof org.hl7.elm.r1.BinaryExpression) { + return map((org.hl7.elm.r1.BinaryExpression) element); + } + + throw new IllegalArgumentException("unknown class of org.hl7.elm.r1.Element: " + element.getClass().getName()); + } +} \ No newline at end of file diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmAnalysisHelper.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmAnalysisHelper.java new file mode 100644 index 000000000..bb16faf94 --- /dev/null +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmAnalysisHelper.java @@ -0,0 +1,301 @@ +package org.cqframework.cql.elm.evaluation; + +import org.cqframework.cql.elm.requirements.ElmRequirementsContext; +import org.hl7.cql.model.IntervalType; +import org.hl7.elm.r1.*; +import org.hl7.elm.r1.Expression; +import org.hl7.fhir.r5.model.*; +import org.hl7.fhir.r5.model.Quantity; + +import java.math.BigDecimal; + +public class ElmAnalysisHelper { + private static DateTimeType toFhirDateTimeValue(ElmRequirementsContext context, Expression value) { + if (value == null) { + return null; + } + + DataType result = toFhirValue(context, value); + if (result instanceof DateTimeType) { + return (DateTimeType)result; + } + if (result instanceof DateType) { + return new DateTimeType(((DateType)result).getValueAsString()); + } + + throw new IllegalArgumentException("Could not convert expression to a DateTime value"); + } + + public static DataType toFhirValue(ElmRequirementsContext context, Expression value) { + if (value == null) { + return null; + } + + // In the special case that the value is directly a parameter ref, use the parameter extension mechanism + if (value instanceof ParameterRef) { + if (context.getTypeResolver().isIntervalType(value.getResultType())) { + Extension e = toExpression(context, (ParameterRef)value); + org.hl7.cql.model.DataType pointType = ((IntervalType)value.getResultType()).getPointType(); + if (context.getTypeResolver().isDateTimeType(pointType) || context.getTypeResolver().isDateType(pointType)) { + Period period = new Period(); + period.addExtension(e); + return period; + } + else if (context.getTypeResolver().isQuantityType(pointType) || context.getTypeResolver().isIntegerType(pointType) || context.getTypeResolver().isDecimalType(pointType)) { + Range range = new Range(); + range.addExtension(e); + return range; + } + else { + throw new IllegalArgumentException(String.format("toFhirValue not implemented for interval of %s", pointType.toString())); + } + } + // Boolean, Integer, Decimal, String, Quantity, Date, DateTime, Time, Coding, CodeableConcept + else if (context.getTypeResolver().isBooleanType(value.getResultType())) { + BooleanType result = new BooleanType(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else if (context.getTypeResolver().isIntegerType(value.getResultType())) { + IntegerType result = new IntegerType(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else if (context.getTypeResolver().isDecimalType(value.getResultType())) { + DecimalType result = new DecimalType(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else if (context.getTypeResolver().isQuantityType(value.getResultType())) { + Quantity result = new Quantity(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else if (context.getTypeResolver().isCodeType(value.getResultType())) { + Coding result = new Coding(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + + } + else if (context.getTypeResolver().isConceptType(value.getResultType())) { + CodeableConcept result = new CodeableConcept(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else if (context.getTypeResolver().isDateType(value.getResultType())) { + DateType result = new DateType(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else if (context.getTypeResolver().isDateTimeType(value.getResultType())) { + DateTimeType result = new DateTimeType(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else if (context.getTypeResolver().isTimeType(value.getResultType())) { + TimeType result = new TimeType(); + result.addExtension(toExpression(context, (ParameterRef)value)); + return result; + } + else { + throw new IllegalArgumentException(String.format("toFhirValue not implemented for parameter of type %s", value.getResultType().toString())); + } + } + + // Attempt to convert the CQL value to a FHIR value: + if (value instanceof Interval) { + // TODO: Handle lowclosed/highclosed + return new Period().setStartElement(toFhirDateTimeValue(context, ((Interval)value).getLow())) + .setEndElement(toFhirDateTimeValue(context, ((Interval)value).getHigh())); + } + else if (value instanceof Literal) { + if (context.getTypeResolver().isDateTimeType(value.getResultType())) { + return new DateTimeType(((Literal)value).getValue()); + } + else if (context.getTypeResolver().isDateType(value.getResultType())) { + return new DateType(((Literal)value).getValue()); + } + else if (context.getTypeResolver().isIntegerType(value.getResultType())) { + return new IntegerType(((Literal)value).getValue()); + } + else if (context.getTypeResolver().isDecimalType(value.getResultType())) { + return new DecimalType(((Literal)value).getValue()); + } + else if (context.getTypeResolver().isStringType(value.getResultType())) { + return new StringType(((Literal)value).getValue()); + } + } + else if (value instanceof DateTime) { + DateTime dateTime = (DateTime)value; + return new DateTimeType(toDateTimeString( + toFhirValue(context, dateTime.getYear()), + toFhirValue(context, dateTime.getMonth()), + toFhirValue(context, dateTime.getDay()), + toFhirValue(context, dateTime.getHour()), + toFhirValue(context, dateTime.getMinute()), + toFhirValue(context, dateTime.getSecond()), + toFhirValue(context, dateTime.getMillisecond()), + toFhirValue(context, dateTime.getTimezoneOffset()) + )); + } + else if (value instanceof org.hl7.elm.r1.Date) { + org.hl7.elm.r1.Date date = (org.hl7.elm.r1.Date)value; + return new DateType(toDateString( + toFhirValue(context, date.getYear()), + toFhirValue(context, date.getMonth()), + toFhirValue(context, date.getDay()) + )); + } + else if (value instanceof Time) { + Time time = (Time)value; + return new TimeType(toTimeString( + toFhirValue(context, time.getHour()), + toFhirValue(context, time.getMinute()), + toFhirValue(context, time.getSecond()), + toFhirValue(context, time.getMillisecond()) + )); + } + else if (value instanceof Start) { + DataType operand = toFhirValue(context, ((Start)value).getOperand()); + if (operand != null) { + Period period = (Period)operand; + return period.getStartElement(); + } + } + else if (value instanceof End) { + DataType operand = toFhirValue(context, ((End)value).getOperand()); + if (operand != null) { + Period period = (Period)operand; + return period.getEndElement(); + } + + } + + throw new IllegalArgumentException(String.format("toFhirValue not implemented for %s", value.getClass().getSimpleName())); + } + + // Can't believe I have to write this, there seriously isn't a String.format option for this!!!! + private static String padLeft(String input, int width, String padWith) { + if (input == null || padWith == null || padWith.length() == 0) { + return null; + } + + // Can't believe I have to do this, why is repeat not available until Java 11!!!!! + while (input.length() < width) { + input = padWith + input; + } + + return input; + } + + private static String padZero(String input, int width) { + return padLeft(input, width, "0"); + } + + // Ugly to have to do this here, but cannot reuse engine evaluation logic without a major refactor + // TODO: Consider refactoring to reuse engine evaluation logic here + private static String toDateTimeString(DataType year, DataType month, DataType day, DataType hour, DataType minute, DataType second, DataType millisecond, DataType timezoneOffset) { + if (year == null) { + return null; + } + + StringBuilder result = new StringBuilder(); + if (year instanceof IntegerType) { + result.append(padZero(((IntegerType)year).getValue().toString(), 4)); + } + if (month instanceof IntegerType) { + result.append("-"); + result.append(padZero(((IntegerType)month).getValue().toString(), 2)); + } + if (day instanceof IntegerType) { + result.append("-"); + result.append(padZero(((IntegerType)day).getValue().toString(), 2)); + } + if (hour instanceof IntegerType) { + result.append("T"); + result.append(padZero(((IntegerType)hour).getValue().toString(), 2)); + } + if (minute instanceof IntegerType) { + result.append(":"); + result.append(padZero(((IntegerType)minute).getValue().toString(), 2)); + } + if (second instanceof IntegerType) { + result.append(":"); + result.append(padZero(((IntegerType)second).getValue().toString(), 2)); + } + if (millisecond instanceof IntegerType) { + result.append("."); + result.append(padZero(((IntegerType)millisecond).getValue().toString(), 3)); + } + if (timezoneOffset instanceof DecimalType) { + BigDecimal offset = ((DecimalType)timezoneOffset).getValue(); + if (offset.intValue() >= 0) { + result.append("+"); + result.append(padZero(Integer.toString(offset.intValue()), 2)); + } + else { + result.append("-"); + result.append(padZero(Integer.toString(Math.abs(offset.intValue())), 2)); + } + int minutes = new BigDecimal("60").multiply(offset.remainder(BigDecimal.ONE)).intValue(); + result.append(":"); + result.append(padZero(Integer.toString(minutes), 2)); + } + + return result.toString(); + } + + private static String toDateString(DataType year, DataType month, DataType day) { + if (year == null) { + return null; + } + + StringBuilder result = new StringBuilder(); + if (year instanceof IntegerType) { + result.append(padZero(((IntegerType)year).getValue().toString(), 4)); + } + if (month instanceof IntegerType) { + result.append("-"); + result.append(padZero(((IntegerType)month).getValue().toString(), 2)); + } + if (day instanceof IntegerType) { + result.append("-"); + result.append(padZero(((IntegerType)day).getValue().toString(), 2)); + } + + return result.toString(); + } + + private static String toTimeString(DataType hour, DataType minute, DataType second, DataType millisecond) { + if (hour == null) { + return null; + } + + StringBuilder result = new StringBuilder(); + if (hour instanceof IntegerType) { + result.append(padZero(((IntegerType)hour).getValue().toString(), 2)); + } + if (minute instanceof IntegerType) { + result.append(":"); + result.append(padZero(((IntegerType)minute).getValue().toString(), 2)); + } + if (second instanceof IntegerType) { + result.append(":"); + result.append(padZero(((IntegerType)second).getValue().toString(), 2)); + } + if (millisecond instanceof IntegerType) { + result.append("."); + result.append(padZero(((IntegerType)millisecond).getValue().toString(), 3)); + } + + return result.toString(); + } + + private static Extension toExpression(ElmRequirementsContext context, ParameterRef parameterRef) { + String expression = parameterRef.getName(); + if (parameterRef.getLibraryName() != null && !parameterRef.getLibraryName().equals(context.getCurrentLibraryIdentifier().getId())) { + expression = String.format("\"%s\".\"%s\"", parameterRef.getLibraryName(), parameterRef.getName()); + } + return new Extension().setUrl("http://hl7.org/fhir/StructureDefinition/cqf-expression").setValue(new org.hl7.fhir.r5.model.Expression().setLanguage("text/cql-identifier").setExpression(expression)); + } +} diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmEvaluationHelper.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmEvaluationHelper.java new file mode 100644 index 000000000..38a39b2ee --- /dev/null +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/evaluation/ElmEvaluationHelper.java @@ -0,0 +1,31 @@ +package org.cqframework.cql.elm.evaluation; + +import org.cqframework.cql.elm.LibraryMapper; +import org.cqframework.cql.elm.execution.IncludeDef; +import org.hl7.elm.r1.Library; +import org.hl7.elm.r1.Expression; +import org.opencds.cqf.cql.engine.execution.*; + +import java.time.ZonedDateTime; +import java.util.Map; + +public class ElmEvaluationHelper { + + // TODO: Improved library loader support... + private static LibraryLoader libraryLoader = new DefaultLibraryLoader(); + + public static Object evaluate(Library library, Expression value, Map parameters, ZonedDateTime evaluationDateTime) { + // TODO: Cache for libraries? + org.cqframework.cql.elm.execution.Library engineLibrary = LibraryMapper.INSTANCE.map(library); + org.cqframework.cql.elm.execution.Expression engineValue = LibraryMapper.INSTANCE.map(value); + + Object result = engineValue.evaluate(getContext(engineLibrary, parameters, evaluationDateTime)); + return result; + } + + private static Context getContext(org.cqframework.cql.elm.execution.Library library, Map parameters, ZonedDateTime evaluationDateTime) { + Context context = evaluationDateTime == null ? new Context(library) : new Context(library, evaluationDateTime); + context.setParameters(library, parameters); + return context; + } +} diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmConjunctiveRequirement.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmConjunctiveRequirement.java index 82fcddd1b..4637c165a 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmConjunctiveRequirement.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmConjunctiveRequirement.java @@ -17,7 +17,7 @@ public List getArguments() { } @Override - public ElmExpressionRequirement combine(ElmExpressionRequirement requirement) { + public ElmExpressionRequirement combine(ElmRequirement requirement) { if (requirement instanceof ElmConjunctiveRequirement) { for (ElmExpressionRequirement argument : ((ElmConjunctiveRequirement)requirement).getArguments()) { arguments.add(argument); @@ -27,8 +27,13 @@ else if (requirement instanceof ElmDisjunctiveRequirement) { // Conjunction of disjunctions, too complex for analysis (i.e. not in DNF) return new ElmExpressionRequirement(this.libraryIdentifier, this.getExpression()); } - else { - arguments.add(requirement); + else if (requirement instanceof ElmExpressionRequirement) { + arguments.add((ElmExpressionRequirement)requirement); + } + else if (requirement instanceof ElmRequirements) { + for (ElmRequirement r : ((ElmRequirements)requirement).getRequirements()) { + combine(r); + } } return this; } diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmDisjunctiveRequirement.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmDisjunctiveRequirement.java index 21b338f2c..e734b7187 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmDisjunctiveRequirement.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmDisjunctiveRequirement.java @@ -17,17 +17,22 @@ public List getArguments() { } @Override - public ElmExpressionRequirement combine(ElmExpressionRequirement requirement) { + public ElmExpressionRequirement combine(ElmRequirement requirement) { if (requirement instanceof ElmDisjunctiveRequirement) { for (ElmExpressionRequirement argument : ((ElmDisjunctiveRequirement)requirement).getArguments()) { arguments.add(argument); } } else if (requirement instanceof ElmConjunctiveRequirement) { - arguments.add(requirement); + arguments.add((ElmExpressionRequirement)requirement); } - else { - arguments.add(requirement); + else if (requirement instanceof ElmExpressionRequirement) { + arguments.add((ElmExpressionRequirement)requirement); + } + else if (requirement instanceof ElmRequirements) { + for (ElmRequirement r : ((ElmRequirements)requirement).getRequirements()) { + combine(r); + } } return this; } diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmExpressionRequirement.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmExpressionRequirement.java index 973c960d9..c1a0e2f58 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmExpressionRequirement.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmExpressionRequirement.java @@ -15,7 +15,7 @@ public Expression getElement() { return getExpression(); } - public ElmExpressionRequirement combine(ElmExpressionRequirement requirement) { + public ElmExpressionRequirement combine(ElmRequirement requirement) { return this; } diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmOperatorRequirement.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmOperatorRequirement.java index 39a96ff90..278dafe56 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmOperatorRequirement.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmOperatorRequirement.java @@ -17,10 +17,15 @@ public ElmOperatorRequirement(VersionedIdentifier libraryIdentifier, Expression } @Override - public ElmExpressionRequirement combine(ElmExpressionRequirement requirement) { - if (requirement != null) { + public ElmExpressionRequirement combine(ElmRequirement requirement) { + if (requirement instanceof ElmExpressionRequirement) { requirements.add(requirement); } + else if (requirement instanceof ElmRequirements) { + for (ElmRequirement r : ((ElmRequirements)requirement).getRequirements()) { + requirements.add(r); + } + } return this; } diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsContext.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsContext.java index ec41d69dd..b4da63aed 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsContext.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsContext.java @@ -8,12 +8,13 @@ import org.hl7.elm.r1.*; import javax.xml.namespace.QName; +import java.time.ZonedDateTime; import java.util.*; import java.util.List; public class ElmRequirementsContext { - public ElmRequirementsContext(LibraryManager libraryManager, CqlTranslatorOptions options, ElmRequirementsVisitor visitor) { + public ElmRequirementsContext(LibraryManager libraryManager, CqlTranslatorOptions options, ElmRequirementsVisitor visitor, Map parameters, ZonedDateTime evaluationDateTime) { if (libraryManager == null) { throw new IllegalArgumentException("Library Manager required"); } @@ -27,6 +28,8 @@ public ElmRequirementsContext(LibraryManager libraryManager, CqlTranslatorOption } this.visitor = visitor; this.requirements = new ElmRequirements(new VersionedIdentifier().withId("result"), new Null()); + this.parameters = parameters; + this.evaluationDateTime = evaluationDateTime; } private CqlTranslatorOptions options; @@ -42,6 +45,16 @@ public LibraryManager getLibraryManager() { return libraryManager; } + private Map parameters; + public Map getParameters() { + return parameters; + } + + private ZonedDateTime evaluationDateTime; + public ZonedDateTime getEvaluationDateTime() { + return evaluationDateTime; + } + private TypeResolver typeResolver; public TypeResolver getTypeResolver() { return this.typeResolver; diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsVisitor.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsVisitor.java index 2262e2884..e164538d5 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsVisitor.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirementsVisitor.java @@ -412,7 +412,7 @@ else if (right instanceof ElmExpressionRequirement && left == null) { case "ProperIncludedIn": default: { super.visitChildren(elm, context); - return new ElmExpressionRequirement(context.getCurrentLibraryIdentifier(), elm); + return new ElmOperatorRequirement(context.getCurrentLibraryIdentifier(), elm); } } } @@ -425,13 +425,13 @@ public ElmRequirement visitBinaryExpression(BinaryExpression elm, ElmRequirement @Override public ElmRequirement visitTernaryExpression(TernaryExpression elm, ElmRequirementsContext context) { super.visitTernaryExpression(elm, context); - return new ElmExpressionRequirement(context.getCurrentLibraryIdentifier(), elm); + return new ElmOperatorRequirement(context.getCurrentLibraryIdentifier(), elm); } @Override public ElmRequirement visitNaryExpression(NaryExpression elm, ElmRequirementsContext context) { super.visitNaryExpression(elm, context); - return new ElmExpressionRequirement(context.getCurrentLibraryIdentifier(), elm); + return new ElmOperatorRequirement(context.getCurrentLibraryIdentifier(), elm); } @Override @@ -476,7 +476,10 @@ public ElmRequirement visitInstance(Instance elm, ElmRequirementsContext context @Override public ElmRequirement visitInterval(Interval elm, ElmRequirementsContext context) { - return super.visitInterval(elm, context); + ElmRequirement result = super.visitInterval(elm, context); + ElmOperatorRequirement finalResult = new ElmOperatorRequirement(context.getCurrentLibraryIdentifier(), elm); + finalResult.combine(result); + return finalResult; } @Override @@ -840,17 +843,17 @@ public ElmRequirement visitDateTimeComponentFrom(DateTimeComponentFrom elm, ElmR @Override public ElmRequirement visitTimeOfDay(TimeOfDay elm, ElmRequirementsContext context) { - return new ElmExpressionRequirement(context.getCurrentLibraryIdentifier(), elm); + return new ElmOperatorRequirement(context.getCurrentLibraryIdentifier(), elm); } @Override public ElmRequirement visitToday(Today elm, ElmRequirementsContext context) { - return new ElmExpressionRequirement(context.getCurrentLibraryIdentifier(), elm); + return new ElmOperatorRequirement(context.getCurrentLibraryIdentifier(), elm); } @Override public ElmRequirement visitNow(Now elm, ElmRequirementsContext context) { - return new ElmExpressionRequirement(context.getCurrentLibraryIdentifier(), elm); + return new ElmOperatorRequirement(context.getCurrentLibraryIdentifier(), elm); } @Override diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessor.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessor.java index 76ab60bdd..85f79c3a6 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessor.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessor.java @@ -1,7 +1,10 @@ package org.cqframework.cql.elm.requirements.fhir; +import ca.uhn.fhir.context.FhirVersionEnum; import org.cqframework.cql.cql2elm.CqlTranslatorOptions; import org.cqframework.cql.cql2elm.LibraryManager; +import org.cqframework.cql.elm.evaluation.ElmAnalysisHelper; +import org.cqframework.cql.elm.evaluation.ElmEvaluationHelper; import org.hl7.cql.model.NamespaceManager; import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.elm.tracking.TrackBack; @@ -13,9 +16,9 @@ import org.hl7.elm.r1.Element; import org.hl7.elm.r1.Expression; import org.hl7.elm.r1.Property; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.model.Library; -import org.hl7.fhir.r5.model.Quantity; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.cqframework.cql.elm.requirements.ElmDataRequirement; import org.cqframework.cql.elm.requirements.ElmPertinenceContext; @@ -23,10 +26,13 @@ import org.cqframework.cql.elm.requirements.ElmRequirements; import org.cqframework.cql.elm.requirements.ElmRequirementsContext; import org.cqframework.cql.elm.requirements.ElmRequirementsVisitor; +import org.opencds.cqf.cql.engine.fhir.converter.FhirTypeConverter; +import org.opencds.cqf.cql.engine.fhir.converter.FhirTypeConverterFactory; import javax.xml.bind.JAXBElement; import java.io.Serializable; import java.math.BigDecimal; +import java.time.ZonedDateTime; import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -47,6 +53,37 @@ public Library gatherDataRequirements(LibraryManager libraryManager, CompiledLib public Library gatherDataRequirements(LibraryManager libraryManager, CompiledLibrary translatedLibrary, CqlTranslatorOptions options, Set expressions, boolean includeLogicDefinitions, boolean recursive) { + return gatherDataRequirements(libraryManager, translatedLibrary, options, expressions, null, null, includeLogicDefinitions, recursive); + } + + public Library gatherDataRequirements(LibraryManager libraryManager, CompiledLibrary translatedLibrary, + CqlTranslatorOptions options, Set expressions, + Map parameters, + boolean includeLogicDefinitions, boolean recursive) { + return gatherDataRequirements(libraryManager, translatedLibrary, options, expressions, parameters, null, includeLogicDefinitions, recursive); + } + + /** + * Gathers the data requirements for the given translated library, returning a Library resource with the + * effective data requirements for the given expression or set of expressions in the given compiled library. + * @param libraryManager The library manager used to support compilation of the library + * @param translatedLibrary The compiled library to gather data requirements from + * @param options The translator options used to compile the library + * @param expressions The expressions to gather data requirements for, null for all expressions in the library + * @param parameters Any parameters to the expressions to be analyzed. If null, analysis will only be performed on ELM, + * whereas if provided, analysis will be performed by attempting to evaluate compile-time evaluable + * data requirements comparands + * @param evaluationDateTime The date time of the evaluation (used to provide the request date time to the engine in the + * case that compile-time evaluable expressions are evaluated + * @param includeLogicDefinitions True to include logic definition extensions in the output containing the source for + * each expression from which data requirements are gathered + * @param recursive True to indicate the data requirements gather should be recursive + * @return + */ + public Library gatherDataRequirements(LibraryManager libraryManager, CompiledLibrary translatedLibrary, + CqlTranslatorOptions options, Set expressions, + Map parameters, ZonedDateTime evaluationDateTime, + boolean includeLogicDefinitions, boolean recursive) { if (libraryManager == null) { throw new IllegalArgumentException("libraryManager required"); } @@ -56,7 +93,7 @@ public Library gatherDataRequirements(LibraryManager libraryManager, CompiledLib } ElmRequirementsVisitor visitor = new ElmRequirementsVisitor(); - ElmRequirementsContext context = new ElmRequirementsContext(libraryManager, options, visitor); + ElmRequirementsContext context = new ElmRequirementsContext(libraryManager, options, visitor, parameters, evaluationDateTime); List expressionDefs = null; if (expressions == null) { @@ -140,11 +177,12 @@ public Library gatherDataRequirements(LibraryManager libraryManager, CompiledLib requirements = requirements.collapse(); } - return createLibrary(context, requirements, translatedLibrary.getIdentifier(), expressionDefs, includeLogicDefinitions); + return createLibrary(context, requirements, translatedLibrary.getIdentifier(), expressionDefs, parameters, evaluationDateTime, includeLogicDefinitions); } private Library createLibrary(ElmRequirementsContext context, ElmRequirements requirements, - VersionedIdentifier libraryIdentifier, Iterable expressionDefs, boolean includeLogicDefinitions) { + VersionedIdentifier libraryIdentifier, Iterable expressionDefs, + Map parameters, ZonedDateTime evaluationDateTime, boolean includeLogicDefinitions) { Library returnLibrary = new Library(); returnLibrary.setStatus(Enumerations.PublicationStatus.ACTIVE); CodeableConcept libraryType = new CodeableConcept(); @@ -585,280 +623,33 @@ private org.hl7.fhir.r5.model.DataRequirement.DataRequirementCodeFilterComponent return cfc; } - // Can't believe I have to write this, there seriously isn't a String.format option for this!!!! - private String padLeft(String input, int width, String padWith) { - if (input == null || padWith == null || padWith.length() == 0) { - return null; - } - - // Can't believe I have to do this, why is repeat not available until Java 11!!!!! - while (input.length() < width) { - input = padWith + input; - } - - return input; - } - - private String padZero(String input, int width) { - return padLeft(input, width, "0"); - } - - // Ugly to have to do this here, but cannot reuse engine evaluation logic without a major refactor - // TODO: Consider refactoring to reuse engine evaluation logic here - private String toDateTimeString(DataType year, DataType month, DataType day, DataType hour, DataType minute, DataType second, DataType millisecond, DataType timezoneOffset) { - if (year == null) { - return null; - } - - StringBuilder result = new StringBuilder(); - if (year instanceof IntegerType) { - result.append(padZero(((IntegerType)year).getValue().toString(), 4)); - } - if (month instanceof IntegerType) { - result.append("-"); - result.append(padZero(((IntegerType)month).getValue().toString(), 2)); - } - if (day instanceof IntegerType) { - result.append("-"); - result.append(padZero(((IntegerType)day).getValue().toString(), 2)); - } - if (hour instanceof IntegerType) { - result.append("T"); - result.append(padZero(((IntegerType)hour).getValue().toString(), 2)); - } - if (minute instanceof IntegerType) { - result.append(":"); - result.append(padZero(((IntegerType)minute).getValue().toString(), 2)); - } - if (second instanceof IntegerType) { - result.append(":"); - result.append(padZero(((IntegerType)second).getValue().toString(), 2)); - } - if (millisecond instanceof IntegerType) { - result.append("."); - result.append(padZero(((IntegerType)millisecond).getValue().toString(), 3)); - } - if (timezoneOffset instanceof DecimalType) { - BigDecimal offset = ((DecimalType)timezoneOffset).getValue(); - if (offset.intValue() >= 0) { - result.append("+"); - result.append(padZero(Integer.toString(offset.intValue()), 2)); - } - else { - result.append("-"); - result.append(padZero(Integer.toString(Math.abs(offset.intValue())), 2)); - } - int minutes = new BigDecimal("60").multiply(offset.remainder(BigDecimal.ONE)).intValue(); - result.append(":"); - result.append(padZero(Integer.toString(minutes), 2)); - } - - return result.toString(); - } - - private String toDateString(DataType year, DataType month, DataType day) { - if (year == null) { - return null; - } - - StringBuilder result = new StringBuilder(); - if (year instanceof IntegerType) { - result.append(padZero(((IntegerType)year).getValue().toString(), 4)); - } - if (month instanceof IntegerType) { - result.append("-"); - result.append(padZero(((IntegerType)month).getValue().toString(), 2)); - } - if (day instanceof IntegerType) { - result.append("-"); - result.append(padZero(((IntegerType)day).getValue().toString(), 2)); - } - - return result.toString(); - } - - private String toTimeString(DataType hour, DataType minute, DataType second, DataType millisecond) { - if (hour == null) { - return null; - } - - StringBuilder result = new StringBuilder(); - if (hour instanceof IntegerType) { - result.append(padZero(((IntegerType)hour).getValue().toString(), 2)); - } - if (minute instanceof IntegerType) { - result.append(":"); - result.append(padZero(((IntegerType)minute).getValue().toString(), 2)); - } - if (second instanceof IntegerType) { - result.append(":"); - result.append(padZero(((IntegerType)second).getValue().toString(), 2)); - } - if (millisecond instanceof IntegerType) { - result.append("."); - result.append(padZero(((IntegerType)millisecond).getValue().toString(), 3)); - } - - return result.toString(); - } - - // TODO: Either handle conversions on a case-by-case, or implement conversion evaluation logic... - private DateTimeType toFhirDateTimeValue(ElmRequirementsContext context, Expression value) { - if (value == null) { - return null; - } - - DataType result = toFhirValue(context, value); - if (result instanceof DateTimeType) { - return (DateTimeType)result; - } - if (result instanceof DateType) { - return new DateTimeType(((DateType)result).getValueAsString()); - } - - throw new IllegalArgumentException("Could not convert expression to a DateTime value"); - } - private DataType toFhirValue(ElmRequirementsContext context, Expression value) { if (value == null) { return null; } - if (value instanceof Interval) { - // TODO: Handle lowclosed/highclosed - return new Period().setStartElement(toFhirDateTimeValue(context, ((Interval)value).getLow())) - .setEndElement(toFhirDateTimeValue(context, ((Interval)value).getHigh())); - } - else if (value instanceof Literal) { - if (context.getTypeResolver().isDateTimeType(value.getResultType())) { - return new DateTimeType(((Literal)value).getValue()); - } - else if (context.getTypeResolver().isDateType(value.getResultType())) { - return new DateType(((Literal)value).getValue()); - } - else if (context.getTypeResolver().isIntegerType(value.getResultType())) { - return new IntegerType(((Literal)value).getValue()); - } - else if (context.getTypeResolver().isDecimalType(value.getResultType())) { - return new DecimalType(((Literal)value).getValue()); - } - else if (context.getTypeResolver().isStringType(value.getResultType())) { - return new StringType(((Literal)value).getValue()); - } + if (context.getParameters() == null) { + return ElmAnalysisHelper.toFhirValue(context, value); } - else if (value instanceof DateTime) { - DateTime dateTime = (DateTime)value; - return new DateTimeType(toDateTimeString( - toFhirValue(context, dateTime.getYear()), - toFhirValue(context, dateTime.getMonth()), - toFhirValue(context, dateTime.getDay()), - toFhirValue(context, dateTime.getHour()), - toFhirValue(context, dateTime.getMinute()), - toFhirValue(context, dateTime.getSecond()), - toFhirValue(context, dateTime.getMillisecond()), - toFhirValue(context, dateTime.getTimezoneOffset()))); - } - else if (value instanceof org.hl7.elm.r1.Date) { - org.hl7.elm.r1.Date date = (org.hl7.elm.r1.Date)value; - return new DateType(toDateString( - toFhirValue(context, date.getYear()), - toFhirValue(context, date.getMonth()), - toFhirValue(context, date.getDay()) - )); - } - else if (value instanceof Start) { - DataType operand = toFhirValue(context, ((Start)value).getOperand()); - if (operand != null) { - Period period = (Period)operand; - return period.getStartElement(); - } - } - else if (value instanceof End) { - DataType operand = toFhirValue(context, ((End)value).getOperand()); - if (operand != null) { - Period period = (Period)operand; - return period.getEndElement(); - } + else { + // Attempt to use an evaluation visitor to evaluate the value (must be compile-time literal or this will produce a runtime error) + Object result = ElmEvaluationHelper.evaluate(context.resolveLibrary(context.getCurrentLibraryIdentifier()).getLibrary(), value, context.getParameters(), context.getEvaluationDateTime()); - } - else if (value instanceof ParameterRef) { - if (context.getTypeResolver().isIntervalType(value.getResultType())) { - Extension e = toExpression(context, (ParameterRef)value); - org.hl7.cql.model.DataType pointType = ((IntervalType)value.getResultType()).getPointType(); - if (context.getTypeResolver().isDateTimeType(pointType) || context.getTypeResolver().isDateType(pointType)) { - Period period = new Period(); - period.addExtension(e); - return period; - } - else if (context.getTypeResolver().isQuantityType(pointType) || context.getTypeResolver().isIntegerType(pointType) || context.getTypeResolver().isDecimalType(pointType)) { - Range range = new Range(); - range.addExtension(e); - return range; - } - else { - throw new IllegalArgumentException(String.format("toFhirValue not implemented for interval of %s", pointType.toString())); - } - } - // Boolean, Integer, Decimal, String, Quantity, Date, DateTime, Time, Coding, CodeableConcept - else if (context.getTypeResolver().isBooleanType(value.getResultType())) { - BooleanType result = new BooleanType(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; - } - else if (context.getTypeResolver().isIntegerType(value.getResultType())) { - IntegerType result = new IntegerType(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; + if (result instanceof DataType) { + return (DataType)result; } - else if (context.getTypeResolver().isDecimalType(value.getResultType())) { - DecimalType result = new DecimalType(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; - } - else if (context.getTypeResolver().isQuantityType(value.getResultType())) { - Quantity result = new Quantity(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; - } - else if (context.getTypeResolver().isCodeType(value.getResultType())) { - Coding result = new Coding(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; + if (result == null) { + return null; } - else if (context.getTypeResolver().isConceptType(value.getResultType())) { - CodeableConcept result = new CodeableConcept(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; - } - else if (context.getTypeResolver().isDateType(value.getResultType())) { - DateType result = new DateType(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; - } - else if (context.getTypeResolver().isDateTimeType(value.getResultType())) { - DateTimeType result = new DateTimeType(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; - } - else if (context.getTypeResolver().isTimeType(value.getResultType())) { - TimeType result = new TimeType(); - result.addExtension(toExpression(context, (ParameterRef)value)); - return result; - } - else { - throw new IllegalArgumentException(String.format("toFhirValue not implemented for parameter of type %s", value.getResultType().toString())); - } - } - throw new IllegalArgumentException(String.format("toFhirValue not implemented for %s", value.getClass().getSimpleName())); - } - private org.hl7.fhir.r5.model.Extension toExpression(ElmRequirementsContext context, ParameterRef parameterRef) { - String expression = parameterRef.getName(); - if (parameterRef.getLibraryName() != null && !parameterRef.getLibraryName().equals(context.getCurrentLibraryIdentifier().getId())) { - expression = String.format("\"%s\".\"%s\"", parameterRef.getLibraryName(), parameterRef.getName()); + FhirTypeConverter converter = new FhirTypeConverterFactory().create(FhirVersionEnum.R5); + IBase fhirResult = converter.toFhirType(result); + if (fhirResult instanceof DataType) { + return (DataType)fhirResult; + } + throw new IllegalArgumentException(String.format("toFhirValue not implemented for result of type %s", result.getClass().getSimpleName())); } - return new Extension().setUrl("http://hl7.org/fhir/StructureDefinition/cqf-expression").setValue(new org.hl7.fhir.r5.model.Expression().setLanguage("text/cql-identifier").setExpression(expression)); } private org.hl7.fhir.r5.model.DataRequirement.DataRequirementDateFilterComponent toDateFilterComponent(ElmRequirementsContext context, VersionedIdentifier libraryIdentifier, String property, Expression value) { @@ -871,6 +662,11 @@ private org.hl7.fhir.r5.model.DataRequirement.DataRequirementDateFilterComponent try { dfc.setValue(toFhirValue(context, value)); } + catch (Exception e) { + Period p = new Period(); + p.addExtension("http://hl7.org/fhir/uv/crmi-analysisException", new StringType(String.format("Error attempting to determine filter value: %s", e.getMessage()))); + dfc.setValue(p); + } finally { context.exitLibrary(); } diff --git a/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/MapperCodeGen.java b/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/MapperCodeGen.java new file mode 100644 index 000000000..b19884d97 --- /dev/null +++ b/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/MapperCodeGen.java @@ -0,0 +1,167 @@ +package org.cqframework.cql.elm; + +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.hl7.elm.r1.Element; +import org.reflections.Reflections; +import org.slf4j.Logger; +// import org.testng.annotations.Test; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Lists; + +// This is just some code that helps bootstrap the library mapper code-gen +// It's a one-off for now, but we could integrate it into the build at some point. +public class MapperCodeGen { + + private static Logger logger = LoggerFactory.getLogger(MapperCodeGen.class); + + // TODO: Write to a file rather than stdout. + // Part of the file is manually generated, so whats the java equivalent of partial classes? Hmm. + public static void main(String[] args) { + + Reflections reflections = new Reflections("org.hl7.elm.r1"); + Set> subTypes = reflections.getSubTypesOf(Element.class); + List> translatorTypeList = Lists.newArrayList(subTypes); + + Reflections engineTypeReflections = new Reflections("org.cqframework.cql.elm.execution"); + Set> engineSubTypes = engineTypeReflections.getSubTypesOf(org.cqframework.cql.elm.execution.Element.class); + List> engineTypeList = Lists.newArrayList(engineSubTypes); + + Reflections engineImplementationReflections = new Reflections("org.opencds.cqf.cql.engine.elm.execution"); + Set> engineImplementationSubTypes = engineImplementationReflections.getSubTypesOf(org.cqframework.cql.elm.execution.Element.class); + List> engineImplementationTypeList = Lists.newArrayList(engineImplementationSubTypes); + + List> concreteTranslatorTypes = translatorTypeList.stream() + .filter(x -> !Modifier.isAbstract(x.getModifiers())).collect(Collectors.toList()); + + Map, Class> typeMap = createConcreteTypeMap(concreteTranslatorTypes, engineTypeList, engineImplementationTypeList); + + + for (Map.Entry, Class> entry : typeMap.entrySet()) { + if (entry.getKey().getSimpleName().equals("Null")) { + continue; + } + + System.out.println(entry.getValue().getName() + " map(" + entry.getKey().getName() + " element);"); + } + + // Get all abstract types + List> polymorphicTranslatorTypes = translatorTypeList.stream() + .filter(x -> Modifier.isAbstract(x.getModifiers())).collect(Collectors.toList()); + for (Class abstractType : polymorphicTranslatorTypes) { + generateFunctionForAbstractType(abstractType, translatorTypeList); + } + + generateFunctionForAbstractType(Element.class, translatorTypeList); + } + + static Map, Class> createConcreteTypeMap(List> translatorTypeList, List> engineTypeList, List> engineImplementationTypeList) { + Map, Class> typeMap = new HashMap<>(); + + // Map all the types that have implementation + for (Class engineClass : engineImplementationTypeList) { + if (engineClass.getSimpleName().toLowerCase().contains("mixin")) { + continue; + } + + if (engineClass.getName().contains("org.cqframework.cql.elm.execution")) { + continue; + } + + Class clazz = engineClass.getSuperclass(); + Optional> translatorType = translatorTypeList.stream().filter(x -> x.getSimpleName().equals(clazz.getSimpleName())).findFirst(); + if (!translatorType.isPresent()) { + logger.info("Could not find translator type for engine implementation type: " + engineClass.getName()); + continue; + } + + typeMap.put(translatorType.get(), engineClass); + } + + // Map all the types that simply code-genned and can only be assigned to itself (IOW, most derived) + for (Class engineClass : engineTypeList) { + + long count = engineTypeList.stream().filter(x -> engineClass.isAssignableFrom(x)).count(); + if (count != 1) { + logger.info("Skipped polymorphic type: " + engineClass.getName()); + continue; + } + + Optional> translatorType = translatorTypeList.stream().filter(x -> x.getSimpleName().equals(engineClass.getSimpleName())).findFirst(); + if (!translatorType.isPresent()) { + logger.info("Could not find translator type for engine type: " + engineClass.getName()); + continue; + } + + if (!typeMap.containsKey(translatorType.get())) { + typeMap.put(translatorType.get(), engineClass); + } + } + + return typeMap; + } + + static void generateFunctionForAbstractType(Class abstractType, List> allTypes) { + + List> concreteSubClasses = allTypes.stream() + .filter(x -> !Modifier.isAbstract(x.getModifiers())).filter(x -> abstractType.isAssignableFrom(x)) + .collect(Collectors.toList()); + + String abstractEngineName = abstractType.getName().replace("org.hl7.elm.r1", + "org.cqframework.cql.elm.execution"); + System.out.println("default " + abstractEngineName + " map(" + abstractType.getName() + " element) {"); + + System.out.println("if(element == null) {"); + System.out.println("\t return null;"); + System.out.println("}"); + System.out.println(); + + concreteSubClasses.sort((x, y) -> + Long.valueOf(concreteSubClasses.stream().filter(z -> x.isAssignableFrom(z)).count()).compareTo(Long.valueOf(concreteSubClasses.stream().filter(z -> y.isAssignableFrom(z)).count()))); + Boolean first = true; + for (Class clazz : concreteSubClasses) { + if (!first) { + System.out.print("\telse if "); + } + else { + System.out.print("\tif "); + } + System.out.println("(element instanceof " + clazz.getName() + ") {"); + System.out.println("\t\treturn map((" + clazz.getName() + ")element);"); + System.out.println("\t}"); + first = false; + } + + List> abstractSubClasses = allTypes.stream() + .filter(x -> Modifier.isAbstract(x.getModifiers())).filter(x -> abstractType.isAssignableFrom(x)) + .collect(Collectors.toList()); + for (Class clazz : abstractSubClasses) { + if (clazz.equals(abstractType)) { + continue; + } + + if (!first) { + System.out.print("\telse if "); + } + else { + System.out.print("\tif "); + } + System.out.println("(element instanceof " + clazz.getName() + ") {"); + System.out.println("\t\treturn map((" + clazz.getName() + ")element);"); + System.out.println("\t}"); + first = false; + } + + System.out.println(); + System.out.println("\tthrow new IllegalArgumentException(\"unknown class of " + abstractType.getName() + ": \" + element.getClass().getName());"); + System.out.println("}"); + } + +} diff --git a/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java b/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java index 6e6f816fa..7ccf2d0b3 100644 --- a/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java +++ b/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java @@ -16,7 +16,10 @@ import java.io.IOException; import java.nio.file.Paths; import java.time.LocalDate; +import java.time.OffsetDateTime; import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.Date; import java.util.List; @@ -565,6 +568,20 @@ private CqlTranslator setupDataRequirementsAnalysis(String fileName, CqlTranslat return translator; } + private org.hl7.fhir.r5.model.Library getModuleDefinitionLibrary(CqlTranslator translator, CqlTranslatorOptions cqlTranslatorOptions, Map parameters) { + DataRequirementsProcessor dqReqTrans = new DataRequirementsProcessor(); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = dqReqTrans.gatherDataRequirements(libraryManager, translator.getTranslatedLibrary(), cqlTranslatorOptions, null, parameters, false,false); + assertTrue(moduleDefinitionLibrary.getType().getCode("http://terminology.hl7.org/CodeSystem/library-type").equalsIgnoreCase("module-definition")); + return moduleDefinitionLibrary; + } + + private org.hl7.fhir.r5.model.Library getModuleDefinitionLibrary(CqlTranslator translator, CqlTranslatorOptions cqlTranslatorOptions, Map parameters, ZonedDateTime evaluationDateTime) { + DataRequirementsProcessor dqReqTrans = new DataRequirementsProcessor(); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = dqReqTrans.gatherDataRequirements(libraryManager, translator.getTranslatedLibrary(), cqlTranslatorOptions, null, parameters, evaluationDateTime, false,false); + assertTrue(moduleDefinitionLibrary.getType().getCode("http://terminology.hl7.org/CodeSystem/library-type").equalsIgnoreCase("module-definition")); + return moduleDefinitionLibrary; + } + private org.hl7.fhir.r5.model.Library getModuleDefinitionLibrary(CqlTranslator translator, CqlTranslatorOptions cqlTranslatorOptions) { DataRequirementsProcessor dqReqTrans = new DataRequirementsProcessor(); org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = dqReqTrans.gatherDataRequirements(libraryManager, translator.getTranslatedLibrary(), cqlTranslatorOptions, null, false); @@ -606,7 +623,7 @@ public void TestFunctionDataRequirements() throws IOException { // [Condition] Iterable expectedDataRequirements = getDataRequirementsForType(moduleDefinitionLibrary.getDataRequirement(), Enumerations.FHIRAllTypes.CONDITION); assertTrue(expectedDataRequirements.iterator().hasNext()); - outputModuleDefinitionLibrary(moduleDefinitionLibrary); + //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } @Test @@ -628,7 +645,7 @@ public void TestNonElectiveInpatientEncounterDataRequirements() throws IOExcepti } } assertTrue(actualDrcf != null); - outputModuleDefinitionLibrary(moduleDefinitionLibrary); + //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } @Test @@ -655,14 +672,14 @@ public void TestAllStrokeEncounterDataRequirements() throws IOException { Iterable conditionDataRequirements = getDataRequirementsForType(moduleDefinitionLibrary.getDataRequirement(), Enumerations.FHIRAllTypes.CONDITION); assertTrue(conditionDataRequirements.iterator().hasNext()); - outputModuleDefinitionLibrary(moduleDefinitionLibrary); + //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } @Test public void TestCMS104DataRequirements() throws IOException { CqlTranslatorOptions translatorOptions = getTranslatorOptions(); CqlTranslator translator = setupDataRequirementsGather("CMS104/DischargedonAntithromboticTherapyFHIR.cql", translatorOptions); - org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, null); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions); // DataRequirements of the All Stroke Encounter expression: // [Encounter: "Non-Elective Inpatient Encounter"] (from Non Elective Inpatient Encounter) @@ -685,7 +702,7 @@ public void TestCMS104DataRequirements() throws IOException { Iterable conditionDataRequirements = getDataRequirementsForType(moduleDefinitionLibrary.getDataRequirement(), Enumerations.FHIRAllTypes.CONDITION); assertTrue(conditionDataRequirements.iterator().hasNext()); - outputModuleDefinitionLibrary(moduleDefinitionLibrary); + //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } @Test @@ -835,7 +852,7 @@ public void TestDataRequirementsAnalysisCase1c() throws IOException { } assertTrue(expectedDataRequirement != null); - outputModuleDefinitionLibrary(moduleDefinitionLibrary); + //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } @Test @@ -951,6 +968,241 @@ public void TestDataRequirementsAnalysisCase2b() throws IOException { //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } + @Test + public void TestDataRequirementsAnalysisCase2e() throws IOException { + CqlTranslatorOptions translatorOptions = getTranslatorOptions(); + CqlTranslator translator = setupDataRequirementsAnalysis("TestCases/TestCase2e.cql", translatorOptions); + // Evaluate this test as of 12/31/2022 + ZonedDateTime evaluationDateTime = ZonedDateTime.of(2022, 12, 31, 0, 0, 0, 0, ZoneId.systemDefault()); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap(), evaluationDateTime); + + /* + 2e - Timing phrase 90 days or less before + DataRequirement + type: Condition + dateFilter: { path: onset, value: Interval[Today() - 90 days, Today()] } + + define "Date Filter Expression": + [Condition] C + where onset as Period starts 90 days or less before Today() + */ + + ExpressionDef ed = translator.getTranslatedLibrary().resolveExpressionRef("Date Filter Expression"); + assertTrue(ed.getExpression() instanceof Query); + Query q = (Query)ed.getExpression(); + assertTrue(q.getSource() != null && q.getSource().size() == 1); + AliasedQuerySource source = q.getSource().get(0); + assertTrue(source.getExpression() instanceof Retrieve); + Retrieve r = (Retrieve)source.getExpression(); + assertTrue(r.getDateFilter() != null && r.getDateFilter().size() == 1); + DateFilterElement dfe = r.getDateFilter().get(0); + assertEquals(dfe.getProperty(), "onset"); + assertTrue(dfe.getValue() instanceof Interval); + + OffsetDateTime expectedPeriodStart = evaluationDateTime.toOffsetDateTime().minusDays(90); + OffsetDateTime expectedPeriodEnd = evaluationDateTime.toOffsetDateTime().minusNanos(1000000); + DataRequirement expectedDataRequirement = null; + boolean hasFilter = false; + for (DataRequirement dr : moduleDefinitionLibrary.getDataRequirement()) { + if (dr.getType() == Enumerations.FHIRAllTypes.CONDITION) { + if (dr.getDateFilter().size() == 1) { + for (DataRequirement.DataRequirementDateFilterComponent dfc : dr.getDateFilter()) { + if ("onset".equals(dfc.getPath())) { + if (dfc.getValue() instanceof Period) { + String expectedPeriodStartString = expectedPeriodStart.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); //"2022-10-02T00:00:00-07:00" + String expectedPeriodEndString = expectedPeriodEnd.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); //"2022-12-30T23:59:59.999-07:00" + if (((Period)dfc.getValue()).hasStart() && ((Period)dfc.getValue()).getStartElement().asStringValue().equals(expectedPeriodStartString) + && ((Period)dfc.getValue()).hasEnd() && ((Period)dfc.getValue()).getEndElement().asStringValue().equals(expectedPeriodEndString)) { + hasFilter = true; + } + } + } + } + + if (hasFilter) { + expectedDataRequirement = dr; + } + } + } + } + assertTrue(expectedDataRequirement != null); + } + + @Test + public void TestDataRequirementsAnalysisCase2g() throws IOException { + CqlTranslatorOptions translatorOptions = getTranslatorOptions(); + CqlTranslator translator = setupDataRequirementsAnalysis("TestCases/TestCase2g.cql", translatorOptions); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap()); + + /* + 2g - Equal to a compile-time literal function + DataRequirement + type: Condition + dateFilter: { path: onset, value: Today() } + + define DateTimeEqualToFunction: + [Condition] C + where C.onset as dateTime = Today() + */ + + ExpressionDef ed = translator.getTranslatedLibrary().resolveExpressionRef("DateTimeEqualToFunction"); + assertTrue(ed.getExpression() instanceof Query); + Query q = (Query)ed.getExpression(); + assertTrue(q.getSource() != null && q.getSource().size() == 1); + AliasedQuerySource source = q.getSource().get(0); + assertTrue(source.getExpression() instanceof Retrieve); + Retrieve r = (Retrieve)source.getExpression(); + assertTrue(r.getDateFilter() != null && r.getDateFilter().size() == 1); + DateFilterElement dfe = r.getDateFilter().get(0); + assertEquals(dfe.getProperty(), "onset"); + assertTrue(dfe.getValue() instanceof Interval); + + DataRequirement expectedDataRequirement = null; + for (DataRequirement dr : moduleDefinitionLibrary.getDataRequirement()) { + if (dr.getType() == Enumerations.FHIRAllTypes.CONDITION) { + if (dr.getDateFilter().size() == 1) { + DataRequirement.DataRequirementDateFilterComponent dfc = dr.getDateFilterFirstRep(); + if ("onset".equals(dfc.getPath())) { + if (dfc.getValue() instanceof Period) { + if (((Period)dfc.getValue()).hasStart() && ((Period)dfc.getValue()).hasEnd()) { + expectedDataRequirement = dr; + } + } + } + } + } + } + assertTrue(expectedDataRequirement != null); + } + + @Test + public void TestDataRequirementsAnalysisCase2i() throws IOException { + CqlTranslatorOptions translatorOptions = getTranslatorOptions(); + CqlTranslator translator = setupDataRequirementsAnalysis("TestCases/TestCase2i.cql", translatorOptions); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap()); + + /* + 2i - In a compile-time literal interval + DataRequirement + type: Condition + dateFilter: { path: onset, value: Interval[@2022-12-31 - 90 days, @2022-12-31] } + + define "Date Filter Expression": + [Condition] C + where C.onset as dateTime in Interval[@2022-12-31 - 90 days, @2022-12-31] + */ + + ZonedDateTime evaluationDateTime = ZonedDateTime.of(2022, 12, 31, 0, 0, 0, 0, ZoneId.systemDefault()); + OffsetDateTime expectedPeriodStart = evaluationDateTime.toOffsetDateTime().minusDays(90); + ExpressionDef ed = translator.getTranslatedLibrary().resolveExpressionRef("Date Filter Expression"); + assertTrue(ed.getExpression() instanceof Query); + Query q = (Query)ed.getExpression(); + assertTrue(q.getSource() != null && q.getSource().size() == 1); + AliasedQuerySource source = q.getSource().get(0); + assertTrue(source.getExpression() instanceof Retrieve); + Retrieve r = (Retrieve)source.getExpression(); + assertTrue(r.getDateFilter() != null && r.getDateFilter().size() == 1); + DateFilterElement dfe = r.getDateFilter().get(0); + assertEquals(dfe.getProperty(), "onset"); + assertTrue(dfe.getValue() instanceof Interval); + + DataRequirement expectedDataRequirement = null; + for (DataRequirement dr : moduleDefinitionLibrary.getDataRequirement()) { + if (dr.getType() == Enumerations.FHIRAllTypes.CONDITION) { + if (dr.getDateFilter().size() == 1) { + DataRequirement.DataRequirementDateFilterComponent dfc = dr.getDateFilterFirstRep(); + if ("onset".equals(dfc.getPath())) { + if (dfc.getValue() instanceof Period) { + String expectedPeriodStartString = expectedPeriodStart.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // "2022-10-02T00:00:00-07:00" + if (((Period)dfc.getValue()).hasStart() && ((Period)dfc.getValue()).hasEnd() && ((Period)dfc.getValue()).getStartElement().asStringValue().equals(expectedPeriodStartString)) { + expectedDataRequirement = dr; + } + } + } + } + } + } + assertTrue(expectedDataRequirement != null); + } + + @Test + public void TestDataRequirementsAnalysisCase2j() throws IOException { + CqlTranslatorOptions translatorOptions = getTranslatorOptions(); + CqlTranslator translator = setupDataRequirementsAnalysis("TestCases/TestCase2j.cql", translatorOptions); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap()); + + /* + 2j - Before and after + DataRequirement + type: Condition + dateFilter: { path: onset, value: Interval[@2022-12-31T - 90 days, @2022-12-31T] } + + define "Date Filter Expression": + [Condition] C + where C.onset as dateTime >= @2022-12-31T - 90 days + and C.onset as dateTime <= @2022-12-31T + */ + + ZonedDateTime evaluationDateTime = ZonedDateTime.of(2022, 12, 31, 0, 0, 0, 0, ZoneId.systemDefault()); + OffsetDateTime expectedPeriodStart1 = evaluationDateTime.toOffsetDateTime().minusDays(90); + OffsetDateTime expectedPeriodEnd1 = ZonedDateTime.of(9999, 12, 31, 23, 59, 59, 999000000, ZoneId.of("UTC")).toOffsetDateTime(); + OffsetDateTime expectedPeriodStart2 = ZonedDateTime.of(1, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC")).toOffsetDateTime(); + OffsetDateTime expectedPeriodEnd2 = evaluationDateTime.toOffsetDateTime(); + ExpressionDef ed = translator.getTranslatedLibrary().resolveExpressionRef("Date Filter Expression"); + assertTrue(ed.getExpression() instanceof Query); + Query q = (Query)ed.getExpression(); + assertTrue(q.getSource() != null && q.getSource().size() == 1); + AliasedQuerySource source = q.getSource().get(0); + assertTrue(source.getExpression() instanceof Retrieve); + Retrieve r = (Retrieve)source.getExpression(); + assertTrue(r.getDateFilter() != null && r.getDateFilter().size() == 2); + DateFilterElement dfe = r.getDateFilter().get(0); + assertEquals(dfe.getProperty(), "onset"); + assertTrue(dfe.getValue() instanceof Interval); + dfe = r.getDateFilter().get(1); + assertEquals(dfe.getProperty(), "onset"); + assertTrue(dfe.getValue() instanceof Interval); + + DataRequirement expectedDataRequirement = null; + boolean hasFilter1 = false; + boolean hasFilter2 = false; + for (DataRequirement dr : moduleDefinitionLibrary.getDataRequirement()) { + if (dr.getType() == Enumerations.FHIRAllTypes.CONDITION) { + if (dr.getDateFilter().size() == 2) { + for (DataRequirement.DataRequirementDateFilterComponent dfc : dr.getDateFilter()) { + if ("onset".equals(dfc.getPath())) { + if (dfc.getValue() instanceof Period) { + String expectedPeriodStart1String = expectedPeriodStart1.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // "2022-10-02T00:00:00-07:00" + String expectedPeriodEnd1String = expectedPeriodEnd1.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // "9999-12-31T23:59:59.999Z" + String expectedPeriodStart2String = expectedPeriodStart2.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // "0001-01-01T00:00:00Z" + String expectedPeriodEnd2String = expectedPeriodEnd2.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // "2022-12-31T00:00:00-07:00" + if (((Period)dfc.getValue()).hasStart() && ((Period)dfc.getValue()).getStartElement().asStringValue().equals(expectedPeriodStart1String) + && ((Period)dfc.getValue()).hasEnd() && ((Period)dfc.getValue()).getEndElement().asStringValue().equals(expectedPeriodEnd1String)) { + hasFilter1 = true; + } + else if (((Period)dfc.getValue()).hasEnd() + && ((Period)dfc.getValue()).hasStart()) { + String actualPeriodStart2String = ((Period)dfc.getValue()).getStartElement().asStringValue(); + String actualPeriodEnd2String = ((Period)dfc.getValue()).getEndElement().asStringValue(); + if (actualPeriodStart2String.equals(expectedPeriodStart2String) && actualPeriodEnd2String.equals(expectedPeriodEnd2String)) { + // && ((Period)dfc.getValue()).getEndElement().asStringValue().equals(expectedPeriodEnd2String) + // && ((Period)dfc.getValue()).getStartElement().asStringValue().equals(expectedPeriodStart2String) + hasFilter2 = true; + } + } + } + } + } + + if (hasFilter1 && hasFilter2) { + expectedDataRequirement = dr; + } + } + } + } + assertTrue(expectedDataRequirement != null); + } + @Test public void TestDataRequirementsAnalysisCase9a() throws IOException { CqlTranslatorOptions translatorOptions = getTranslatorOptions(); @@ -1256,7 +1508,7 @@ public void TestDataRequirementsAnalysisCase9f() throws IOException { //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } - @Test + //@Test public void TestDataRequirementsAnalysisCase10a() throws IOException { CqlTranslatorOptions translatorOptions = getTranslatorOptions(); CqlTranslator translator = setupDataRequirementsAnalysis("TestCases/TestCase10a.cql", translatorOptions); @@ -1431,7 +1683,7 @@ public void TestWithDependencies() throws IOException { CqlTranslatorOptions translatorOptions = getTranslatorOptions(); translatorOptions.setAnalyzeDataRequirements(false); CqlTranslator translator = setupDataRequirementsAnalysis("WithDependencies/BSElements.cql", translatorOptions); - org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap(), ZonedDateTime.of(2023, 1, 16, 0, 0, 0, 0, ZoneId.of("UTC"))); assertNotNull(moduleDefinitionLibrary); assertEqualToExpectedModuleDefinitionLibrary(moduleDefinitionLibrary, "WithDependencies/Library-BSElements-data-requirements.json"); diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/DataRequirements.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/DataRequirements.cql index df559980a..de8737cf8 100644 --- a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/DataRequirements.cql +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/DataRequirements.cql @@ -131,18 +131,20 @@ define "HospiceEncounterClaimsBUnboundDate": where exists CEncounter.item IEncounter where date from IEncounter.serviced."start" in day of "Measurement Period" - /* - 2b - Bound Measurement Period - Interval[@2019-01-01, @2020-01-01 ) - DataRequirement - type: Claim - dateFilter: { path: item.serviced.start, valuePeriod: "@2019-01-01-@2020-01-01" }, - */ - -/* define "HospiceEncounterClaimsBBoundDate": +/* +2b - Bound Measurement Period +Interval[@2019-01-01, @2020-01-01 ) +DataRequirement +type: Claim +dateFilter: { path: item.serviced.start, valuePeriod: "@2019-01-01-@2020-01-01" }, +*/ + +/* +define "HospiceEncounterClaimsBBoundDate": [Claim] CEncounter where exists CEncounter.item IEncounter - where IEncounter.serviced."start" in "Measurement Period" */ + where IEncounter.serviced."start" in "Measurement Period" +*/ /* 2c - Bound Measurement Period @@ -152,10 +154,12 @@ type: Claim dateFilter: { path: item.serviced.start, valuePeriod: "@2019-01-01T00:00:00.0-@2020-01-01T00:00:00.0" }, */ -/* define "HospiceEncounterClaimsBBoundDateFrom": +/* +define "HospiceEncounterClaimsBBoundDateFrom": [Claim] CEncounter where exists CEncounter.item IEncounter - where date from IEncounter.serviced."start" in "Measurement Period" */ + where date from IEncounter.serviced."start" in "Measurement Period" +*/ /* @@ -165,12 +169,97 @@ DataRequirement type: Claim */ -/* define "HospiceEncounterClaimsBBoundDateFromPlus1Day": - [Claim] CEncounter - where exists CEncounter.item IEncounter - where (date from IEncounter.serviced."start") + 1 day in "Measurement Period" */ +/* +define "HospiceEncounterClaimsBBoundDateFromPlus1Day": + [Claim] CEncounter + where exists CEncounter.item IEncounter + where (date from IEncounter.serviced."start") + 1 day in "Measurement Period" +*/ + +/* +define “cxInjury”: + (CommonEl.“Get Active Confirmed Conditions”([Condition])) C + where C.code in CommonCx.“rc_cervicalSpineInjury” + and FHIRCommon.ToInterval(C.onset) starts 30 days or less before Today() +*/ + +/* +2e - Timing phrase 90 days or less before +DataRequirement +type: Condition +dateFilter: { path: onset, value: Interval[@2022-12-31T - 90 days, @2022-12-31T] } +*/ + +/* +define "Date Filter Expression": + [Condition] C + where C.onset as Period starts 90 days or less before Today() + +Emits as: +define "Date Filter Expression": + [Condition] C + where start of (C.onset as Period) in Interval[Today() - 90 days, Today()] +*/ + +/* +2f - +*/ + +/* +define DateTimeEqualToFunction: + [Condition] C + where FHIRCommon.ToInterval(C.onset) starts 90 days or less before Today() +*/ + +/* +2g - Equal to a compile-time literal function +DataRequirement +type: Condition +dateFilter: { path: onset, value: Today() } +*/ + +/* +define "Date Filter Expression": + [Condition] C + where C.onset as dateTime = Today() +*/ + +/* +2h +*/ + +/* +define "Date Filter Expression": + [Condition] C + where C.onset as dateTime = Now() +*/ + +/* +2i - In a compile-time literal interval +DataRequirement +type: Condition +dateFilter: { path: onset, value: Interval[@2022-12-31 - 90 days, @2022-12-31] } +*/ +/* +define "Date Filter Expression": + [Condition] C + where C.onset as dateTime in Interval[@2022-12-31T - 90 days, @2022-12-31T] +*/ +/* +2j - Before and after +DataRequirement +type: Condition +dateFilter: { path: onset, value: Interval[@2022-12-31T - 90 days, @2022-12-31T] } +*/ + +/* +define "Date Filter Expression": + [Condition] C + where C.onset as dateTime >= @2022-12-31T - 90 days + and C.onset as dateTime <= @2022-12-31T +*/ /* 2. diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2e.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2e.cql new file mode 100644 index 000000000..9e3115377 --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2e.cql @@ -0,0 +1,25 @@ +library TestCase2e + +using FHIR version '4.0.1' + +include FHIRHelpers version '4.0.1' + +context Patient + +/* +2e - Timing phrase 90 days or less before +DataRequirement +type: Condition +dateFilter: { path: onset, value: Interval[Today() - 90 days, Today()] } +*/ + +define "Date Filter Expression": + [Condition] C + where C.onset as Period starts 90 days or less before Today() + +/* +Emits as: +define "Date Filter Expression": + [Condition] C + where start of (onset as Period) in Interval[Today() - 90 days, Today()] +*/ diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2g.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2g.cql new file mode 100644 index 000000000..c634fdaeb --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2g.cql @@ -0,0 +1,20 @@ +library TestCase2b + +using FHIR version '4.0.1' + +include FHIRHelpers version '4.0.1' + +parameter "Measurement Period" Interval default Interval[@2019-01-01, @2020-01-01) + +context Patient + +/* +2g - Equal to a compile-time literal function +DataRequirement +type: Condition +dateFilter: { path: onset, value: Today() } +*/ + +define DateTimeEqualToFunction: + [Condition] C + where C.onset as dateTime = Today() diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2i.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2i.cql new file mode 100644 index 000000000..408bb3613 --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2i.cql @@ -0,0 +1,18 @@ +library TestCase2i + +using FHIR version '4.0.1' + +include FHIRHelpers version '4.0.1' + +context Patient + +/* +2i - In a compile-time literal interval +DataRequirement +type: Condition +dateFilter: { path: onset, value: Interval[@2022-12-31 - 90 days, @2022-12-31] } +*/ + +define "Date Filter Expression": + [Condition] C + where C.onset as dateTime in Interval[@2022-12-31T - 90 days, @2022-12-31T] diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2j.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2j.cql new file mode 100644 index 000000000..5fa322586 --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/TestCases/TestCase2j.cql @@ -0,0 +1,20 @@ +library TestCase2j + +using FHIR version '4.0.1' + +include FHIRHelpers version '4.0.1' + +context Patient + +/* +2j - Before and after +DataRequirement +type: Condition +dateFilter: { path: onset, value: Interval[@2022-12-31T - 90 days, @2022-12-31T] } +*/ + +define "Date Filter Expression": + [Condition] C + where C.onset as dateTime >= @2022-12-31T - 90 days + and C.onset as dateTime <= @2022-12-31T + diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/WithDependencies/Library-BSElements-data-requirements.json b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/WithDependencies/Library-BSElements-data-requirements.json index 1501caca2..313f9d749 100644 --- a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/WithDependencies/Library-BSElements-data-requirements.json +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/WithDependencies/Library-BSElements-data-requirements.json @@ -31,30 +31,6 @@ "type": "depends-on", "display": "Library Ind2E31A37EB104A7D1", "resource": "Library/Ind2E31A37EB104A7D1|1.0.000" - }, { - "type": "depends-on", - "display": "Value set rp_biliopancreaticDiversionWithDuodenalSwitch_cambia", - "resource": "http://example.com/fhir/ValueSet/rp_biliopancreaticDiversionWithDuodenalSwitch_cambia" - }, { - "type": "depends-on", - "display": "Value set ro_bodyMassIndex_kg_per_m2", - "resource": "http://example.com/fhir/ValueSet/ro_bodyMassIndex_kg_per_m2" - }, { - "type": "depends-on", - "display": "Value set ro_weight_kg", - "resource": "http://example.com/fhir/ValueSet/ro_weight_kg" - }, { - "type": "depends-on", - "display": "Value set ro_height_cm", - "resource": "http://example.com/fhir/ValueSet/ro_height_cm" - }, { - "type": "depends-on", - "display": "Value set ro_weight_lb", - "resource": "http://example.com/fhir/ValueSet/ro_weight_lb" - }, { - "type": "depends-on", - "display": "Value set ro_height_in", - "resource": "http://example.com/fhir/ValueSet/ro_height_in" } ], "parameter": [ { "name": "Patient", @@ -72,24 +48,6 @@ "dataRequirement": [ { "type": "Patient", "profile": [ "http://hl7.org/fhir/StructureDefinition/Patient" ] - }, { - "type": "ServiceRequest", - "profile": [ "http://hl7.org/fhir/StructureDefinition/ServiceRequest" ], - "mustSupport": [ "code", "authoredOn" ], - "codeFilter": [ { - "path": "code", - "valueSet": "http://example.com/fhir/ValueSet/rp_biliopancreaticDiversionWithDuodenalSwitch_cambia" - } ] - }, { - "type": "Observation", - "profile": [ "http://hl7.org/fhir/StructureDefinition/Observation" ], - "mustSupport": [ "category" ], - "codeFilter": [ { - "path": "category", - "code": [ { - "code": "vital-sign" - } ] - } ] }, { "type": "Observation", "profile": [ "http://hl7.org/fhir/StructureDefinition/Observation" ], @@ -102,6 +60,13 @@ }, { "path": "code", "valueSet": "http://example.com/fhir/ValueSet/ro_bodyMassIndex_kg_per_m2" + } ], + "dateFilter": [ { + "path": "effective", + "valuePeriod": { + "start": "2022-07-20T00:00:00Z", + "end": "2023-01-15T23:59:59.999Z" + } } ] }, { "type": "Observation", @@ -115,6 +80,13 @@ }, { "path": "code", "valueSet": "http://example.com/fhir/ValueSet/ro_weight_kg" + } ], + "dateFilter": [ { + "path": "effective", + "valuePeriod": { + "start": "2022-07-20T00:00:00Z", + "end": "2023-01-15T23:59:59.999Z" + } } ] }, { "type": "Observation", @@ -128,6 +100,13 @@ }, { "path": "code", "valueSet": "http://example.com/fhir/ValueSet/ro_height_cm" + } ], + "dateFilter": [ { + "path": "effective", + "valuePeriod": { + "start": "2022-01-16T00:00:00Z", + "end": "2023-01-15T23:59:59.999Z" + } } ] }, { "type": "Observation", @@ -141,6 +120,13 @@ }, { "path": "code", "valueSet": "http://example.com/fhir/ValueSet/ro_weight_lb" + } ], + "dateFilter": [ { + "path": "effective", + "valuePeriod": { + "start": "2022-07-20T00:00:00Z", + "end": "2023-01-15T23:59:59.999Z" + } } ] }, { "type": "Observation", @@ -154,6 +140,13 @@ }, { "path": "code", "valueSet": "http://example.com/fhir/ValueSet/ro_height_in" + } ], + "dateFilter": [ { + "path": "effective", + "valuePeriod": { + "start": "2022-01-16T00:00:00Z", + "end": "2023-01-15T23:59:59.999Z" + } } ] } ] } \ No newline at end of file diff --git a/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEngine.java b/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEngine.java index c7563b4bc..efeda661d 100644 --- a/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEngine.java +++ b/Src/java/elm/src/main/java/org/cqframework/cql/elm/evaluating/SimpleElmEngine.java @@ -2,6 +2,9 @@ import org.hl7.elm.r1.*; +import javax.xml.namespace.QName; +import java.math.BigDecimal; + /* A simple ELM engine that is capable of limited evaluation of ELM required for static analysis and other optimization use cases (such as @@ -14,74 +17,209 @@ public SimpleElmEngine() { private boolean literalsEqual(Literal left, Literal right) { return (left == null && right == null) - || (left.getValueType() != null && left.getValueType().equals(right.getValueType()) && stringsEqual(left.getValue(), right.getValue())); + || (left != null && left.getValueType() != null && left.getValueType().equals(right.getValueType()) && stringsEqual(left.getValue(), right.getValue())); } public boolean booleansEqual(Expression left, Expression right) { + return expressionsEqual(left, right); + } + + public boolean integersEqual(Expression left, Expression right) { + return expressionsEqual(left, right); + } + + public boolean decimalsEqual(Expression left, Expression right) { + return expressionsEqual(left, right); + } + + public boolean decimalsEqual(BigDecimal left, BigDecimal right) { if (left == null && right == null) { return true; } - if (left instanceof Literal) { - if (right instanceof Literal) { - return literalsEqual((Literal)left, (Literal)right); - } + if (left == null || right == null) { + return false; } - return false; + return left.equals(right); } - public boolean integersEqual(Expression left, Expression right) { + public boolean quantitiesEqual(Quantity left, Quantity right) { if (left == null && right == null) { return true; } - if (left instanceof Literal) { - if (right instanceof Literal) { - return literalsEqual((Literal)left, (Literal)right); - } + if (left == null || right == null) { + return true; } - return false; + return decimalsEqual(left.getValue(), right.getValue()) + && stringsEqual(left.getUnit(), right.getUnit()); } - public boolean decimalsEqual(Expression left, Expression right) { + public boolean stringsEqual(Expression left, Expression right) { + return expressionsEqual(left, right); + } + + public boolean dateTimesEqual(Expression left, Expression right) { + return expressionsEqual(left, right); + } + + public boolean dateRangesEqual(Expression left, Expression right) { + return expressionsEqual(left, right); + } + + public boolean stringsEqual(String left, String right) { + return (left == null && right == null) || (left != null && left.equals(right)); + } + + public boolean systemsEqual(CodeSystemRef left, CodeSystemRef right) { + // TODO: Needs to do the comparison on the URI, but I don't want to have to resolve here + return (left == null && right == null) + || (left != null && stringsEqual(left.getLibraryName(), right.getLibraryName()) && stringsEqual(left.getName(), right.getName())); + } + + public boolean valueSetsEqual(ValueSetRef left, ValueSetRef right) { + // TODO: Needs to do the comparison on the URI, but I don't want to have to resolve here + return (left == null && right == null) + || (left != null && stringsEqual(left.getLibraryName(), right.getLibraryName()) && stringsEqual(left.getName(), right.getName())); + } + + public boolean codesEqual(Expression left, Expression right) { + return expressionsEqual(left, right); + } + + public boolean qnamesEqual(QName left, QName right) { if (left == null && right == null) { return true; } - if (left instanceof Literal) { - if (right instanceof Literal) { - return literalsEqual((Literal)left, (Literal)right); - } + if (left == null || right == null) { + return false; } - return false; + return left.equals(right); } - public boolean stringsEqual(Expression left, Expression right) { + public boolean typeSpecifiersEqual(TypeSpecifier left, TypeSpecifier right) { if (left == null && right == null) { return true; } - if (left instanceof Literal) { - if (right instanceof Literal) { - return literalsEqual((Literal)left, (Literal)right); + if (left == null || right == null) { + return false; + } + + // NamedTypeSpecifier + if (left instanceof NamedTypeSpecifier) { + if (right instanceof NamedTypeSpecifier) { + NamedTypeSpecifier leftArg = (NamedTypeSpecifier)left; + NamedTypeSpecifier rightArg = (NamedTypeSpecifier)right; + return qnamesEqual(leftArg.getName(), rightArg.getName()); + } + + return false; + } + + // IntervalTypeSpecifier + if (left instanceof IntervalTypeSpecifier) { + if (right instanceof IntervalTypeSpecifier) { + IntervalTypeSpecifier leftArg = (IntervalTypeSpecifier)left; + IntervalTypeSpecifier rightArg = (IntervalTypeSpecifier)right; + return typeSpecifiersEqual(leftArg.getPointType(), rightArg.getPointType()); + } + + return false; + } + + // ListTypeSpecifier + if (left instanceof ListTypeSpecifier) { + if (right instanceof ListTypeSpecifier) { + ListTypeSpecifier leftArg = (ListTypeSpecifier)left; + ListTypeSpecifier rightArg = (ListTypeSpecifier)right; + return typeSpecifiersEqual(leftArg.getElementType(), rightArg.getElementType()); + } + + return false; + } + + // TupleTypeSpecifier + if (left instanceof TupleTypeSpecifier) { + if (right instanceof TupleTypeSpecifier) { + TupleTypeSpecifier leftArg = (TupleTypeSpecifier)left; + TupleTypeSpecifier rightArg = (TupleTypeSpecifier)right; + if (leftArg.getElement() != null && rightArg.getElement() != null && leftArg.getElement().size() == rightArg.getElement().size()) { + for (int i = 0; i < leftArg.getElement().size(); i++) { + TupleElementDefinition leftElement = leftArg.getElement().get(i); + TupleElementDefinition rightElement = rightArg.getElement().get(i); + if (!typeSpecifiersEqual(leftElement.getType(), rightElement.getType()) + || !typeSpecifiersEqual(leftElement.getElementType(), rightElement.getElementType()) + || !stringsEqual(leftElement.getName(), rightElement.getName())) { + return false; + } + } + + return true; + } + + return false; + } + + return false; + } + + // ChoiceTypeSpecifier + if (left instanceof ChoiceTypeSpecifier) { + if (right instanceof ChoiceTypeSpecifier) { + ChoiceTypeSpecifier leftArg = (ChoiceTypeSpecifier)left; + ChoiceTypeSpecifier rightArg = (ChoiceTypeSpecifier)right; + if (leftArg.getType() != null && rightArg.getType() != null && leftArg.getType().size() == rightArg.getType().size()) { + for (int i = 0; i < leftArg.getType().size(); i++) { + TypeSpecifier leftType = leftArg.getType().get(i); + TypeSpecifier rightType = rightArg.getType().get(i); + if (!typeSpecifiersEqual(leftType, rightType)) { + return false; + } + } + } + + if (leftArg.getChoice() != null && rightArg.getChoice() != null && leftArg.getChoice().size() == rightArg.getChoice().size()) { + for (int i = 0; i < leftArg.getChoice().size(); i++) { + TypeSpecifier leftType = leftArg.getChoice().get(i); + TypeSpecifier rightType = rightArg.getChoice().get(i); + if (!typeSpecifiersEqual(leftType, rightType)) { + return false; + } + } + + return true; + } + + return false; } + + return false; } + // False for the possibility of an unrecognized type specifier type return false; } - public boolean dateTimesEqual(Expression left, Expression right) { + public boolean expressionsEqual(Expression left, Expression right) { if (left == null && right == null) { return true; } + if (left == null || right == null) { + return false; + } + if (left instanceof Literal) { if (right instanceof Literal) { return literalsEqual((Literal)left, (Literal)right); } + + return false; } if (left instanceof Date) { @@ -93,6 +231,8 @@ public boolean dateTimesEqual(Expression left, Expression right) { && integersEqual(leftDate.getMonth(), rightDate.getMonth()) && integersEqual(leftDate.getDay(), rightDate.getDay()); } + + return false; } if (left instanceof Time) { @@ -105,6 +245,8 @@ && integersEqual(leftTime.getMinute(), rightTime.getMinute()) && integersEqual(leftTime.getSecond(), rightTime.getSecond()) && integersEqual(leftTime.getMillisecond(), rightTime.getMillisecond()); } + + return false; } if (left instanceof DateTime) { @@ -121,14 +263,8 @@ && integersEqual(leftDateTime.getSecond(), rightDateTime.getSecond()) && integersEqual(leftDateTime.getMillisecond(), rightDateTime.getMillisecond()) && decimalsEqual(leftDateTime.getTimezoneOffset(), rightDateTime.getTimezoneOffset()); } - } - return false; - } - - public boolean dateRangesEqual(Expression left, Expression right) { - if (left == null && right == null) { - return true; + return false; } if (left instanceof Interval) { @@ -143,6 +279,8 @@ && booleansEqual(leftInterval.getHighClosedExpression(), rightInterval.getHighCl && dateTimesEqual(leftInterval.getHigh(), rightInterval.getHigh()) && leftInterval.isHighClosed() == rightInterval.isHighClosed(); } + + return false; } // TODO: Strictly speaking this would need to resolve the parameter library since it's not in the ELM if it's a local parameter reference @@ -153,49 +291,27 @@ && dateTimesEqual(leftInterval.getHigh(), rightInterval.getHigh()) return stringsEqual(leftParameter.getLibraryName(), rightParameter.getLibraryName()) && stringsEqual(leftParameter.getName(), rightParameter.getName()); } - } - - return false; - } - - public boolean stringsEqual(String left, String right) { - return (left == null && right == null) || (left != null && left.equals(right)); - } - - public boolean systemsEqual(CodeSystemRef left, CodeSystemRef right) { - // TODO: Needs to do the comparison on the URI, but I don't want to have to resolve here - return (left == null && right == null) - || (stringsEqual(left.getLibraryName(), right.getLibraryName()) && stringsEqual(left.getName(), right.getName())); - } - - public boolean valueSetsEqual(ValueSetRef left, ValueSetRef right) { - // TODO: Needs to do the comparison on the URI, but I don't want to have to resolve here - return (left == null && right == null) - || (stringsEqual(left.getLibraryName(), right.getLibraryName()) && stringsEqual(left.getName(), right.getName())); - } - - public boolean codesEqual(Expression left, Expression right) { - if (left == null && right == null) { - return true; - } - if (left instanceof Literal) { - if (right instanceof Literal) { - return literalsEqual((Literal)left, (Literal)right); - } + return false; } if (left instanceof ValueSetRef) { if (right instanceof ValueSetRef) { return valueSetsEqual((ValueSetRef)left, (ValueSetRef)right); } + + return false; } - else if (left instanceof CodeSystemRef) { + + if (left instanceof CodeSystemRef) { if (right instanceof CodeSystemRef) { return systemsEqual((CodeSystemRef)left, (CodeSystemRef)right); } + + return false; } - else if (left instanceof ConceptRef) { + + if (left instanceof ConceptRef) { if (right instanceof ConceptRef) { ConceptRef leftConcept = (ConceptRef)left; ConceptRef rightConcept = (ConceptRef)right; @@ -203,8 +319,11 @@ else if (left instanceof ConceptRef) { return stringsEqual(leftConcept.getLibraryName(), rightConcept.getLibraryName()) && stringsEqual(leftConcept.getName(), rightConcept.getName()); } + + return false; } - else if (left instanceof CodeRef) { + + if (left instanceof CodeRef) { if (right instanceof CodeRef) { CodeRef leftCode = (CodeRef)left; CodeRef rightCode = (CodeRef)right; @@ -212,16 +331,22 @@ else if (left instanceof CodeRef) { return stringsEqual(leftCode.getLibraryName(), rightCode.getLibraryName()) && stringsEqual(leftCode.getName(), rightCode.getName()); } + + return false; } - else if (left instanceof Code) { + + if (left instanceof Code) { if (right instanceof Code) { Code leftCode = (Code)left; Code rightCode = (Code)right; return stringsEqual(leftCode.getCode(), rightCode.getCode()) && systemsEqual(leftCode.getSystem(), rightCode.getSystem()); } + + return false; } - else if (left instanceof Concept) { + + if (left instanceof Concept) { if (right instanceof Concept) { Concept leftConcept = (Concept)left; Concept rightConcept = (Concept)right; @@ -235,12 +360,15 @@ else if (left instanceof Concept) { } } } + + return false; } - else if (left instanceof List) { + + if (left instanceof List) { if (right instanceof List) { List leftList = (List)left; List rightList = (List)right; - // TOOD: Potentially use a hashSet to avoid order-dependence here + // TODO: Potentially use a hashSet to avoid order-dependence here if (leftList.getElement().size() == rightList.getElement().size()) { for (int i = 0; i < leftList.getElement().size(); i++) { if (!codesEqual(leftList.getElement().get(i), rightList.getElement().get(i))) { @@ -249,16 +377,1075 @@ else if (left instanceof List) { } } } + + return false; } - else if (left instanceof ToList) { + if (left instanceof ToList) { if (right instanceof ToList) { Expression leftSingleton = ((ToList)left).getOperand(); Expression rightSingleton = ((ToList)right).getOperand(); return codesEqual(leftSingleton, rightSingleton); } + + return false; } - return false; + // Quantity + if (left instanceof Quantity) { + if (right instanceof Quantity) { + return quantitiesEqual((Quantity)left, (Quantity)right); + } + + return false; + } + + // Ratio + if (left instanceof Ratio) { + if (right instanceof Ratio) { + return quantitiesEqual(((Ratio)left).getDenominator(), ((Ratio)right).getDenominator()) + && quantitiesEqual(((Ratio)left).getNumerator(), ((Ratio)right).getNumerator()); + } + + return false; + } + + // TODO: Consider refactoring ComparableElmRequirement? + // Retrieve + + // InCodeSystem + if (left instanceof InCodeSystem) { + if (right instanceof InCodeSystem) { + InCodeSystem inCodeSystemLeft = (InCodeSystem)left; + InCodeSystem inCodeSystemRight = (InCodeSystem)right; + return expressionsEqual(inCodeSystemLeft.getCode(), inCodeSystemRight.getCode()) + && systemsEqual(inCodeSystemLeft.getCodesystem(), inCodeSystemRight.getCodesystem()) + && expressionsEqual(inCodeSystemLeft.getCodesystemExpression(), inCodeSystemRight.getCodesystemExpression()); + } + + return false; + } + + // AnyInCodeSystem + if (left instanceof AnyInCodeSystem) { + if (right instanceof AnyInCodeSystem) { + AnyInCodeSystem anyInCodeSystemLeft = (AnyInCodeSystem)left; + AnyInCodeSystem anyInCodeSystemRight = (AnyInCodeSystem)right; + return expressionsEqual(anyInCodeSystemLeft.getCodes(), anyInCodeSystemRight.getCodes()) + && systemsEqual(anyInCodeSystemLeft.getCodesystem(), anyInCodeSystemRight.getCodesystem()) + && expressionsEqual(anyInCodeSystemLeft.getCodesystemExpression(), anyInCodeSystemRight.getCodesystemExpression()); + } + + return false; + } + + // InValueSet + if (left instanceof InValueSet) { + if (right instanceof InValueSet) { + InValueSet inLeft = (InValueSet)left; + InValueSet inRight = (InValueSet)right; + return expressionsEqual(inLeft.getCode(), inRight.getCode()) + && valueSetsEqual(inLeft.getValueset(), inRight.getValueset()) + && expressionsEqual(inLeft.getValuesetExpression(), inRight.getValuesetExpression()); + } + + return false; + } + + // AnyInValueSet + if (left instanceof AnyInValueSet) { + if (right instanceof AnyInValueSet) { + AnyInValueSet inLeft = (AnyInValueSet)left; + AnyInValueSet inRight = (AnyInValueSet)right; + return expressionsEqual(inLeft.getCodes(), inRight.getCodes()) + && valueSetsEqual(inLeft.getValueset(), inRight.getValueset()) + && expressionsEqual(inLeft.getValuesetExpression(), inRight.getValuesetExpression()); + } + + return false; + } + + // CalculateAge + if (left instanceof CalculateAge) { + if (right instanceof CalculateAge) { + CalculateAge leftArg = (CalculateAge)left; + CalculateAge rightArg = (CalculateAge)right; + return expressionsEqual(leftArg.getOperand(), rightArg.getOperand()) + && leftArg.getPrecision().equals(rightArg.getPrecision()); + } + + return false; + } + + // Subsumes + if (left instanceof Subsumes) { + if (right instanceof Subsumes) { + Subsumes leftArg = (Subsumes)left; + Subsumes rightArg = (Subsumes)right; + if (operandsEqual(leftArg, rightArg)) { + return true; + } + + return false; + } + + return false; + } + + // SubsumedBy + if (left instanceof SubsumedBy) { + if (right instanceof SubsumedBy) { + SubsumedBy leftArg = (SubsumedBy)left; + SubsumedBy rightArg = (SubsumedBy)right; + if (operandsEqual(leftArg, rightArg)) { + return true; + } + + return false; + } + + return false; + } + + // AggregateExpression + if (left instanceof AggregateExpression) { + if (right instanceof AggregateExpression) { + AggregateExpression leftArg = (AggregateExpression)left; + AggregateExpression rightArg = (AggregateExpression)right; + return aggregateExpressionsEqual(leftArg, rightArg); + } + + return false; + } + + // OperatorExpression + if (left instanceof OperatorExpression) { + if (right instanceof OperatorExpression) { + OperatorExpression leftArg = (OperatorExpression)left; + OperatorExpression rightArg = (OperatorExpression)right; + return operatorExpressionsEqual(leftArg, rightArg); + } + + return false; + } + + if (!left.getClass().getCanonicalName().equals(right.getClass().getCanonicalName())) { + return false; + } + + // AliasRef + if (left instanceof AliasRef) { + if (right instanceof AliasRef) { + AliasRef leftArg = (AliasRef)left; + AliasRef rightArg = (AliasRef)right; + return stringsEqual(leftArg.getName(), rightArg.getName()); + } + + return false; + } + + // Case + if (left instanceof Case) { + if (right instanceof Case) { + Case leftArg = (Case)left; + Case rightArg = (Case)right; + if (!expressionsEqual(leftArg.getComparand(), rightArg.getComparand())) { + return false; + } + + if (!expressionsEqual(leftArg.getElse(), rightArg.getElse())) { + return false; + } + + if (leftArg.getCaseItem() != null && rightArg.getCaseItem() != null && leftArg.getCaseItem().size() == rightArg.getCaseItem().size()) { + for (int i = 0; i < leftArg.getCaseItem().size(); i++) { + CaseItem leftCaseItem = leftArg.getCaseItem().get(i); + CaseItem rightCaseItem = rightArg.getCaseItem().get(i); + if (!expressionsEqual(leftCaseItem.getWhen(), rightCaseItem.getWhen()) || !expressionsEqual(leftCaseItem.getThen(), rightCaseItem.getThen())) { + return false; + } + } + + return true; + } + + return false; + } + + return false; + } + + // Current + if (left instanceof Current) { + if (right instanceof Current) { + Current leftArg = (Current)left; + Current rightArg = (Current)right; + return stringsEqual(leftArg.getScope(), rightArg.getScope()); + } + + return false; + } + + // FunctionRef + if (left instanceof FunctionRef) { + if (right instanceof FunctionRef) { + FunctionRef leftArg = (FunctionRef)left; + FunctionRef rightArg = (FunctionRef)right; + return stringsEqual(leftArg.getLibraryName(), rightArg.getLibraryName()) + && stringsEqual(leftArg.getName(), rightArg.getName()) + && operandsEqual(leftArg, rightArg); + } + } + + // ExpressionRef + if (left instanceof ExpressionRef) { + if (right instanceof ExpressionRef) { + ExpressionRef leftArg = (ExpressionRef)left; + ExpressionRef rightArg = (ExpressionRef)right; + return stringsEqual(leftArg.getLibraryName(), rightArg.getLibraryName()) + && stringsEqual(leftArg.getName(), rightArg.getName()); + } + + return false; + } + + // Filter + if (left instanceof Filter) { + if (right instanceof Filter) { + Filter leftArg = (Filter)left; + Filter rightArg = (Filter)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && expressionsEqual(leftArg.getCondition(), rightArg.getCondition()) + && stringsEqual(leftArg.getScope(), rightArg.getScope()); + } + + return false; + } + + // ForEach + if (left instanceof ForEach) { + if (right instanceof ForEach) { + ForEach leftArg = (ForEach)left; + ForEach rightArg = (ForEach)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && expressionsEqual(leftArg.getElement(), rightArg.getElement()) + && stringsEqual(leftArg.getScope(), rightArg.getScope()); + } + + return false; + } + + // IdentifierRef + if (left instanceof IdentifierRef) { + if (right instanceof IdentifierRef) { + IdentifierRef leftArg = (IdentifierRef)left; + IdentifierRef rightArg = (IdentifierRef)right; + return stringsEqual(leftArg.getLibraryName(), rightArg.getLibraryName()) + && stringsEqual(leftArg.getName(), rightArg.getName()); + } + + return false; + } + + // If + if (left instanceof If) { + if (right instanceof If) { + If leftArg = (If)left; + If rightArg = (If)right; + return expressionsEqual(leftArg.getCondition(), rightArg.getCondition()) + && expressionsEqual(leftArg.getThen(), rightArg.getThen()) + && expressionsEqual(leftArg.getElse(), rightArg.getElse()); + } + + return false; + } + + // Instance + if (left instanceof Instance) { + if (right instanceof Instance) { + Instance leftArg = (Instance)left; + Instance rightArg = (Instance)right; + if (!qnamesEqual(leftArg.getClassType(), rightArg.getClassType())) { + return false; + } + + if (leftArg.getElement() != null && rightArg.getElement() != null && leftArg.getElement().size() == rightArg.getElement().size()) { + for (int i = 0; i < leftArg.getElement().size(); i++) { + InstanceElement leftElement = leftArg.getElement().get(i); + InstanceElement rightElement = rightArg.getElement().get(i); + if (!stringsEqual(leftElement.getName(), rightElement.getName()) || !expressionsEqual(leftElement.getValue(), rightElement.getValue())) { + return false; + } + } + + return true; + } + + return false; + } + + return false; + } + + // Iteration + if (left instanceof Iteration) { + if (right instanceof Iteration) { + Iteration leftArg = (Iteration)left; + Iteration rightArg = (Iteration)right; + return stringsEqual(leftArg.getScope(), rightArg.getScope()); + } + + return false; + } + + // MaxValue + if (left instanceof MaxValue) { + if (right instanceof MaxValue) { + MaxValue leftArg = (MaxValue)left; + MaxValue rightArg = (MaxValue)right; + return qnamesEqual(leftArg.getValueType(), rightArg.getValueType()); + } + + return false; + } + + // MinValue + if (left instanceof MinValue) { + if (right instanceof MinValue) { + MinValue leftArg = (MinValue)left; + MinValue rightArg = (MinValue)right; + return qnamesEqual(leftArg.getValueType(), rightArg.getValueType()); + } + + return false; + } + + // Null + if (left instanceof Null) { + if (right instanceof Null) { + return true; + } + + return false; + } + + // OperandRef + if (left instanceof OperandRef) { + if (right instanceof OperandRef) { + OperandRef leftArg = (OperandRef)left; + OperandRef rightArg = (OperandRef)right; + return stringsEqual(leftArg.getName(), rightArg.getName()); + } + + return false; + } + + // Property + if (left instanceof Property) { + if (right instanceof Property) { + Property leftArg = (Property)left; + Property rightArg = (Property)right; + return stringsEqual(leftArg.getScope(), rightArg.getScope()) + && stringsEqual(leftArg.getPath(), rightArg.getPath()); + } + + return false; + } + + // Query + if (left instanceof Query) { + if (right instanceof Query) { + Query leftArg = (Query)left; + Query rightArg = (Query)right; + } + + return false; + } + + // QueryLetRef + if (left instanceof QueryLetRef) { + if (right instanceof QueryLetRef) { + QueryLetRef leftArg = (QueryLetRef)left; + QueryLetRef rightArg = (QueryLetRef)right; + return stringsEqual(leftArg.getName(), rightArg.getName()); + } + + return false; + } + + // Repeat + if (left instanceof Repeat) { + if (right instanceof Repeat) { + Repeat leftArg = (Repeat)left; + Repeat rightArg = (Repeat)right; + } + + return false; + } + + // Sort + if (left instanceof Sort) { + if (right instanceof Sort) { + Sort leftArg = (Sort)left; + Sort rightArg = (Sort)right; + } + + return false; + } + + // Total + if (left instanceof Total) { + if (right instanceof Total) { + Total leftArg = (Total)left; + Total rightArg = (Total)right; + } + + return false; + } + + // Tuple + if (left instanceof Tuple) { + if (right instanceof Tuple) { + Tuple leftArg = (Tuple)left; + Tuple rightArg = (Tuple)right; + } + + return false; + } + + return false; + } + + public boolean operandsEqual(FunctionRef left, FunctionRef right) { + if (left.getOperand() != null && left.getOperand() != null && left.getOperand().size() == left.getOperand().size()) { + for (int i = 0; i < left.getOperand().size(); i++) { + if (!expressionsEqual(left.getOperand().get(i), right.getOperand().get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + public boolean operandsEqual(BinaryExpression left, BinaryExpression right) { + if (left.getOperand() != null && left.getOperand() != null && left.getOperand().size() == left.getOperand().size()) { + for (int i = 0; i < left.getOperand().size(); i++) { + if (!expressionsEqual(left.getOperand().get(i), right.getOperand().get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + public boolean operandsEqual(TernaryExpression left, TernaryExpression right) { + if (left.getOperand() != null && left.getOperand() != null && left.getOperand().size() == left.getOperand().size()) { + for (int i = 0; i < left.getOperand().size(); i++) { + if (!expressionsEqual(left.getOperand().get(i), right.getOperand().get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + public boolean operandsEqual(NaryExpression left, NaryExpression right) { + if (left.getOperand() != null && left.getOperand() != null && left.getOperand().size() == left.getOperand().size()) { + for (int i = 0; i < left.getOperand().size(); i++) { + if (!expressionsEqual(left.getOperand().get(i), right.getOperand().get(i))) { + return false; + } + } + + return true; + } + + return false; + } + + public boolean operatorExpressionsEqual(OperatorExpression left, OperatorExpression right) { + if (left == null && right == null) { + return true; + } + + if (left == null || right == null) { + return false; + } + + // UnaryExpression + if (left instanceof UnaryExpression) { + if (right instanceof UnaryExpression) { + UnaryExpression leftArg = (UnaryExpression)left; + UnaryExpression rightArg = (UnaryExpression)right; + return unaryExpressionsEqual(leftArg, rightArg); + } + + return false; + } + + // BinaryExpression + if (left instanceof BinaryExpression) { + if (right instanceof BinaryExpression) { + BinaryExpression leftArg = (BinaryExpression)left; + BinaryExpression rightArg = (BinaryExpression)right; + return binaryExpressionsEqual(leftArg, rightArg); + } + + return false; + } + + // TernaryExpression + if (left instanceof TernaryExpression) { + if (right instanceof TernaryExpression) { + TernaryExpression leftArg = (TernaryExpression)left; + TernaryExpression rightArg = (TernaryExpression)right; + return ternaryExpressionsEqual(leftArg, rightArg); + } + + return false; + } + + // NaryExpression + if (left instanceof NaryExpression) { + if (right instanceof NaryExpression) { + NaryExpression leftArg = (NaryExpression)left; + NaryExpression rightArg = (NaryExpression)right; + return naryExpressionsEqual(leftArg, rightArg); + } + + return false; + } + + if (!left.getClass().getCanonicalName().equals(right.getClass().getCanonicalName())) { + return false; + } + + // Round + if (left instanceof Round) { + if (right instanceof Round) { + Round leftArg = (Round)left; + Round rightArg = (Round)right; + return expressionsEqual(leftArg.getOperand(), rightArg.getOperand()) + && expressionsEqual(leftArg.getPrecision(), rightArg.getPrecision()); + } + + return false; + } + + // Combine + if (left instanceof Combine) { + if (right instanceof Combine) { + Combine leftArg = (Combine)left; + Combine rightArg = (Combine)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && expressionsEqual(leftArg.getSeparator(), rightArg.getSeparator()); + } + + return false; + } + + // Split + if (left instanceof Split) { + if (right instanceof Split) { + Split leftArg = (Split)left; + Split rightArg = (Split)right; + return expressionsEqual(leftArg.getStringToSplit(), rightArg.getStringToSplit()) + && expressionsEqual(leftArg.getSeparator(), rightArg.getSeparator()); + } + + return false; + } + + // SplitOnMatches + if (left instanceof SplitOnMatches) { + if (right instanceof SplitOnMatches) { + SplitOnMatches leftArg = (SplitOnMatches)left; + SplitOnMatches rightArg = (SplitOnMatches)right; + return expressionsEqual(leftArg.getStringToSplit(), rightArg.getStringToSplit()) + && expressionsEqual(leftArg.getSeparatorPattern(), rightArg.getSeparatorPattern()); + } + + return false; + } + + // PositionOf + if (left instanceof PositionOf) { + if (right instanceof PositionOf) { + PositionOf leftArg = (PositionOf)left; + PositionOf rightArg = (PositionOf)right; + return expressionsEqual(leftArg.getString(), rightArg.getString()) + && expressionsEqual(leftArg.getPattern(), rightArg.getPattern()); + } + + return false; + } + + // LastPositionOf + if (left instanceof LastPositionOf) { + if (right instanceof LastPositionOf) { + LastPositionOf leftArg = (LastPositionOf)left; + LastPositionOf rightArg = (LastPositionOf)right; + return expressionsEqual(leftArg.getString(), rightArg.getString()) + && expressionsEqual(leftArg.getPattern(), rightArg.getPattern()); + } + + return false; + } + + // Substring + if (left instanceof Substring) { + if (right instanceof Substring) { + Substring leftArg = (Substring)left; + Substring rightArg = (Substring)right; + return expressionsEqual(leftArg.getStringToSub(), rightArg.getStringToSub()) + && expressionsEqual(leftArg.getStartIndex(), rightArg.getStartIndex()) + && expressionsEqual(leftArg.getLength(), rightArg.getLength()); + } + + return false; + } + + // TimeOfDay + // Today + // Now + + // Time + if (left instanceof Time) { + if (right instanceof Time) { + Time leftArg = (Time)left; + Time rightArg = (Time)right; + return expressionsEqual(leftArg.getHour(), rightArg.getHour()) + && expressionsEqual(leftArg.getMinute(), rightArg.getMinute()) + && expressionsEqual(leftArg.getSecond(), rightArg.getSecond()) + && expressionsEqual(leftArg.getMillisecond(), rightArg.getMillisecond()); + } + + return false; + } + + // Date + if (left instanceof Date) { + if (right instanceof Date) { + Date leftArg = (Date)left; + Date rightArg = (Date)right; + return expressionsEqual(leftArg.getYear(), rightArg.getYear()) + && expressionsEqual(leftArg.getMonth(), rightArg.getMonth()) + && expressionsEqual(leftArg.getDay(), rightArg.getDay()); + } + + return false; + } + + // DateTime + if (left instanceof DateTime) { + if (right instanceof DateTime) { + DateTime leftArg = (DateTime)left; + DateTime rightArg = (DateTime)right; + return expressionsEqual(leftArg.getYear(), rightArg.getYear()) + && expressionsEqual(leftArg.getMonth(), rightArg.getMonth()) + && expressionsEqual(leftArg.getDay(), rightArg.getDay()) + && expressionsEqual(leftArg.getHour(), rightArg.getHour()) + && expressionsEqual(leftArg.getMinute(), rightArg.getMinute()) + && expressionsEqual(leftArg.getSecond(), rightArg.getSecond()) + && expressionsEqual(leftArg.getMillisecond(), rightArg.getMillisecond()) + && expressionsEqual(leftArg.getTimezoneOffset(), rightArg.getTimezoneOffset()); + } + + return false; + } + + // First + if (left instanceof First) { + if (right instanceof First) { + First leftArg = (First)left; + First rightArg = (First)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && stringsEqual(leftArg.getOrderBy(), rightArg.getOrderBy()); + } + + return false; + } + + // Last + if (left instanceof Last) { + if (right instanceof Last) { + Last leftArg = (Last)left; + Last rightArg = (Last)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && stringsEqual(leftArg.getOrderBy(), rightArg.getOrderBy()); + } + + return false; + } + + // IndexOf + if (left instanceof IndexOf) { + if (right instanceof IndexOf) { + IndexOf leftArg = (IndexOf)left; + IndexOf rightArg = (IndexOf)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && expressionsEqual(leftArg.getElement(), rightArg.getElement()); + } + + return false; + } + + // Slice + if (left instanceof Slice) { + if (right instanceof Slice) { + Slice leftArg = (Slice)left; + Slice rightArg = (Slice)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && expressionsEqual(leftArg.getStartIndex(), rightArg.getStartIndex()) + && expressionsEqual(leftArg.getEndIndex(), rightArg.getEndIndex()); + } + + return false; + } + + // Children + if (left instanceof Children) { + if (right instanceof Children) { + Children leftArg = (Children)left; + Children rightArg = (Children)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()); + } + + return false; + } + + // Descendents + if (left instanceof Descendents) { + if (right instanceof Descendents) { + Descendents leftArg = (Descendents)left; + Descendents rightArg = (Descendents)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()); + } + + return false; + } + + // Message + if (left instanceof Message) { + if (right instanceof Message) { + Message leftArg = (Message)left; + Message rightArg = (Message)right; + return expressionsEqual(leftArg.getSource(), rightArg.getSource()) + && expressionsEqual(leftArg.getCode(), rightArg.getCode()) + && expressionsEqual(leftArg.getCondition(), rightArg.getCondition()) + && expressionsEqual(leftArg.getMessage(), rightArg.getMessage()) + && expressionsEqual(leftArg.getSeverity(), rightArg.getSeverity()); + } + + return false; + } + + // Generally speaking we would return false here, but because we've covered all the cases, we return true + return true; + } + + public boolean operandsEqual(UnaryExpression left, UnaryExpression right) { + return expressionsEqual(left.getOperand(), right.getOperand()); + } + + public boolean unaryExpressionsEqual(UnaryExpression left, UnaryExpression right) { + if (left == null && right == null) { + return true; + } + + if (left == null || right == null) { + return false; + } + + if (!left.getClass().getCanonicalName().equals(right.getClass().getCanonicalName())) { + return false; + } + + if (!operandsEqual(left, right)) { + return false; + } + + // Abs + // As + if (left instanceof As) { + if (right instanceof As) { + As leftArg = (As)left; + As rightArg = (As)right; + return qnamesEqual(leftArg.getAsType(), rightArg.getAsType()) + && typeSpecifiersEqual(leftArg.getAsTypeSpecifier(), rightArg.getAsTypeSpecifier()) + && leftArg.isStrict() == rightArg.isStrict(); + } + } + // Ceiling + // CanConvert + if (left instanceof CanConvert) { + if (right instanceof CanConvert) { + CanConvert leftArg = (CanConvert)left; + CanConvert rightArg = (CanConvert) right; + return qnamesEqual(leftArg.getToType(), rightArg.getToType()) + && typeSpecifiersEqual(leftArg.getToTypeSpecifier(), rightArg.getToTypeSpecifier()); + } + + return false; + } + // Convert + if (left instanceof Convert) { + if (right instanceof Convert) { + Convert leftArg = (Convert)left; + Convert rightArg = (Convert) right; + return qnamesEqual(leftArg.getToType(), rightArg.getToType()) + && typeSpecifiersEqual(leftArg.getToTypeSpecifier(), rightArg.getToTypeSpecifier()); + } + + return false; + } + // ConvertsToBoolean + // ConvertsToDate + // ConvertsToDateTime + // ConvertsToDecimal + // ConvertsToInteger + // ConvertsToLong + // ConvertsToQuantity + // ConvertsToRatio + // ConvertsToString + // ConvertsToTime + // DateFrom + // DateTimeComponentFrom + if (left instanceof DateTimeComponentFrom) { + if (right instanceof DateTimeComponentFrom) { + DateTimeComponentFrom leftArg = (DateTimeComponentFrom)left; + DateTimeComponentFrom rightArg = (DateTimeComponentFrom)right; + return leftArg.getPrecision() == rightArg.getPrecision(); + } + return false; + } + // Distinct + // End + // Exists + // Exp + // Flatten + // Floor + // Is + if (left instanceof Is) { + if (right instanceof Is) { + Is leftArg = (Is)left; + Is rightArg = (Is)right; + return qnamesEqual(leftArg.getIsType(), rightArg.getIsType()) + && typeSpecifiersEqual(leftArg.getIsTypeSpecifier(), rightArg.getIsTypeSpecifier()); + } + return false; + } + // IsFalse + // IsNull + // IsTrue + // Length + // Ln + // Lower + // Negate + // Not + // PointFrom + // Precision + // Predecessor + // SingletonFrom + // Size + // Start + // Successor + // TimeFrom + // TimezoneFrom + // TimezoneOffsetFrom + // ToBoolean + // ToConcept + // ToChars + // ToDate + // ToDateTime + // ToDecimal + // ToInteger + // ToLong + // ToList + // ToQuantity + // ToRatio + // ToString + // ToTime + // Truncate + // Upper + // Width + + // We've covered all the special cases above, so if we make it here, the expressions are equal + return true; + } + + public boolean binaryExpressionsEqual(BinaryExpression left, BinaryExpression right) { + if (left == null && right == null) { + return true; + } + + if (left == null || right == null) { + return false; + } + + if (!left.getClass().getCanonicalName().equals(right.getClass().getCanonicalName())) { + return false; + } + + if (!operandsEqual(left, right)) { + return false; + } + + // TODO: Handle special cases for operators that have a precision modifier + // Add + // After + // And + // Before + // CanConvertQuantity + // Contains + // ConvertQuantity + // Collapse + // DifferenceBetween + // Divide + // DurationBetween + // Ends + // EndsWith + // Equal + // Equivalent + // Expand + // Greater + // GreaterOrEqual + // HighBoundary + // Implies + // In + // IncludedIn + // Includes + // Indexer + // Less + // LessOrEqual + // Log + // LowBoundary + // Matches + // Meets + // MeetsAfter + // MeetsBefore + // Modulo + // Multiply + // NotEqual + // Or + // Overlaps + // OverlapsAfter + // OverlapsBefore + // Power + // ProperContains + // ProperIn + // ProperIncludedIn + // ProperIncludes + // SameAs + // SameOrAfter + // SameOrBefore + // Starts + // StartsWith + // Subtract + // Times + // TruncatedDivide + // Xor + + return true; + } + + public boolean ternaryExpressionsEqual(TernaryExpression left, TernaryExpression right) { + if (left == null && right == null) { + return true; + } + + if (left == null || right == null) { + return false; + } + + if (!left.getClass().getCanonicalName().equals(right.getClass().getCanonicalName())) { + return false; + } + + if (!operandsEqual(left, right)) { + return false; + } + + // ReplaceMatches + return true; + } + + public boolean naryExpressionsEqual(NaryExpression left, NaryExpression right) { + if (left == null && right == null) { + return true; + } + + if (left == null || right == null) { + return false; + } + + if (!left.getClass().getCanonicalName().equals(right.getClass().getCanonicalName())) { + return false; + } + + if (!operandsEqual(left, right)) { + return false; + } + + // Coalesce + // Concatenate + // Except + // Intersect + // Union + return false; + } + + public boolean aggregateExpressionsEqual(AggregateExpression left, AggregateExpression right) { + if (left == null && right == null) { + return true; + } + + if (left == null || right == null) { + return false; + } + + if (!left.getClass().getCanonicalName().equals(right.getClass().getCanonicalName())) { + return false; + } + + if (!expressionsEqual(left.getSource(), right.getSource()) || !stringsEqual(left.getPath(), right.getPath())) { + return false; + } + + // Aggregate + if (left instanceof Aggregate) { + if (right instanceof Aggregate) { + Aggregate leftArg = (Aggregate)left; + Aggregate rightArg = (Aggregate)right; + return expressionsEqual(leftArg.getInitialValue(), rightArg.getInitialValue()) + && expressionsEqual(leftArg.getIteration(), rightArg.getIteration()); + } + } + // Count + // Sum + // Product + // Min + // Max + // Avg + // GeometricMean + // Median + // Mode + // Variance + // StdDev + // PopulationVariance + // PopulationStdDev + // AllTrue + // AnyTrue + + return true; } } diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java index f30a1b06c..677772647 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/BaseFhirTypeConverter.java @@ -104,6 +104,94 @@ public IBase toFhirType(Object value) { } } + /** + * Determines whether the given string is a CQL calendar unit + * @param unit + * @return true if the given unit is a CQL calendar unit + */ + public boolean isCqlCalendarUnit(String unit) { + if (unit == null) { + return false; + } + + switch (unit) { + case "milliseconds": + case "millisecond": + case "seconds": + case "second": + case "minutes": + case "minute": + case "hours": + case "hour": + case "days": + case "day": + case "weeks": + case "week": + case "months": + case "month": + case "years": + case "year": return true; + default: return false; + } + } + + /** + * Converts the given CQL unit to a UCUM definite-time duration unit according to the table + * and process defined in the CQL specification: https://cql.hl7.org/02-authorsguide.html#quantities + * @param unit + * @return An equivalent UCUM unit for the given CQL calendar duration unit, if the input is a + * CQL calendar duration unit, otherwise returns the input unit. + */ + public String toUcumUnit(String unit) { + if (unit == null) { + return null; + } + + switch (unit) { + case "milliseconds": + case "millisecond": return "ms"; + case "seconds": + case "second": return "s"; + case "minutes": + case "minute": return "min"; + case "hours": + case "hour": return "h"; + case "days": + case "day": return "d"; + case "weeks": + case "week": return "wk"; + case "months": + case "month": return "mo"; + case "years": + case "year": return "a"; + default: return unit; + } + } + + /** + * Converts a Ucum unit to the equivalent CQL unit according to the table defined in the + * CQL specification: https://cql.hl7.org/02-authorsguide.html#quantities + * @param unit + * @return A CQL calendar unit if the input unit is a Ucum definite-duration unit, otherwise, the input unit + */ + public String toCqlCalendarUnit(String unit) { + if (unit == null) { + return null; + } + + switch (unit) { + case "ms": return "millisecond"; + case "s": return "second"; + case "min": return "minute"; + case "h": return "hour"; + case "d": return "day"; + case "wk": return "week"; + case "mo": return "month"; + case "a": return "year"; + default: return unit; + } + } + @Override public ICompositeType toFhirInterval(Interval value) { if (value == null) { diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java index 84bfaa3b5..7689f4293 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2FhirTypeConverter.java @@ -72,9 +72,7 @@ public IPrimitiveType toFhirDateTime(DateTime value) { return null; } - DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME; - java.util.Date date = java.util.Date.from(Instant.from(dtf.parse(value.getDateTime().toString()))); - return new DateTimeType(date); + return new DateTimeType(value.getDateTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); } @Override @@ -101,8 +99,12 @@ public ICompositeType toFhirQuantity(Quantity value) { return null; } + String unit = value.getUnit(); + String system = isCqlCalendarUnit(unit) ? "http://hl7.org/fhirpath/CodeSystem/calendar-units" : "http://unitsofmeasure.org"; + String ucumUnit = toUcumUnit(unit); + return new org.hl7.fhir.dstu2.model.Quantity() - .setSystem("http://unitsofmeasure.org").setCode(value.getUnit()).setValue(value.getValue()); + .setSystem(system).setCode(ucumUnit).setValue(value.getValue()).setUnit(unit); } @Override @@ -165,15 +167,17 @@ public ICompositeType toFhirPeriod(Interval value) { Period period = new Period(); if (getSimpleName(value.getPointType().getTypeName()).equals("DateTime")) { if (value.getStart() != null) { - period.setStart(toFhirDateTime((DateTime) value.getStart()).getValue()); + period.setStartElement((DateTimeType)toFhirDateTime((DateTime)value.getStart())); } if (value.getEnd() != null) { - period.setEnd(toFhirDateTime((DateTime) value.getEnd()).getValue()); + period.setEndElement((DateTimeType)toFhirDateTime((DateTime)value.getEnd())); } return period; } else if (getSimpleName(value.getPointType().getTypeName()).equals("Date")) { + // TODO: This will construct DateTimeType values in FHIR with the system timezone id, not the + // timezoneoffset of the evaluation request..... this is a bug waiting to happen if (value.getStart() != null) { period.setStart(toFhirDate((Date) value.getStart()).getValue()); } @@ -232,7 +236,10 @@ public Quantity toCqlQuantity(ICompositeType value) { } org.hl7.fhir.dstu2.model.Quantity quantity = (org.hl7.fhir.dstu2.model.Quantity) value; - return new Quantity().withUnit(quantity.getUnit()).withValue(quantity.getValue()); + if (quantity.hasComparator()) { + throw new IllegalArgumentException("Cannot convert a FHIR Quantity with a comparator to a CQL quantity"); + } + return new Quantity().withUnit(toCqlCalendarUnit(quantity.getUnit())).withValue(quantity.getValue()); } @Override diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java index 05dab47c6..d2c130c96 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3FhirTypeConverter.java @@ -70,9 +70,7 @@ public IPrimitiveType toFhirDateTime(DateTime value) { return null; } - DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME; - java.util.Date date = java.util.Date.from(Instant.from(dtf.parse(value.getDateTime().toString()))); - return new DateTimeType(date); + return new DateTimeType(value.getDateTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); } @Override @@ -99,8 +97,12 @@ public ICompositeType toFhirQuantity(Quantity value) { return null; } + String unit = value.getUnit(); + String system = isCqlCalendarUnit(unit) ? "http://hl7.org/fhirpath/CodeSystem/calendar-units" : "http://unitsofmeasure.org"; + String ucumUnit = toUcumUnit(unit); + return new org.hl7.fhir.dstu3.model.Quantity() - .setSystem("http://unitsofmeasure.org").setCode(value.getUnit()).setValue(value.getValue()); + .setSystem(system).setCode(ucumUnit).setValue(value.getValue()).setUnit(unit); } @Override @@ -163,15 +165,17 @@ public ICompositeType toFhirPeriod(Interval value) { Period period = new Period(); if (getSimpleName(value.getPointType().getTypeName()).equals("DateTime")) { if (value.getStart() != null) { - period.setStart(toFhirDateTime((DateTime) value.getStart()).getValue()); + period.setStartElement((DateTimeType)toFhirDateTime((DateTime)value.getStart())); } if (value.getEnd() != null) { - period.setEnd(toFhirDateTime((DateTime) value.getEnd()).getValue()); + period.setEndElement((DateTimeType)toFhirDateTime((DateTime)value.getEnd())); } return period; } else if (getSimpleName(value.getPointType().getTypeName()).equals("Date")) { + // TODO: This will construct DateTimeType values in FHIR with the system timezone id, not the + // timezoneoffset of the evaluation request..... this is a bug waiting to happen if (value.getStart() != null) { period.setStart(toFhirDate((Date) value.getStart()).getValue()); } @@ -230,7 +234,10 @@ public Quantity toCqlQuantity(ICompositeType value) { } org.hl7.fhir.dstu3.model.Quantity quantity = (org.hl7.fhir.dstu3.model.Quantity) value; - return new Quantity().withUnit(quantity.getUnit()).withValue(quantity.getValue()); + if (quantity.hasComparator()) { + throw new IllegalArgumentException("Cannot convert a FHIR Quantity with a comparator to a CQL quantity"); + } + return new Quantity().withUnit(toCqlCalendarUnit(quantity.getUnit())).withValue(quantity.getValue()); } @Override diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java index d8f0062e5..4293d357e 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java @@ -130,6 +130,30 @@ public interface FhirTypeConverter { */ public ICompositeType toFhirQuantity(Quantity value); + /** + * Determines whether the given string is a CQL calendar unit + * @param unit + * @return true if the given unit is a CQL calendar unit + */ + public boolean isCqlCalendarUnit(String unit); + + /** + * Converts the given CQL unit to a UCUM definite-time duration unit according to the table + * and process defined in the CQL specification: https://cql.hl7.org/02-authorsguide.html#quantities + * @param unit + * @return An equivalent UCUM unit for the given CQL calendar duration unit, if the input is a + * CQL calendar duration unit, otherwise returns the input unit. + */ + public String toUcumUnit(String unit); + + /** + * Converts a Ucum unit to the equivalent CQL unit according to the table defined in the + * CQL specification: https://cql.hl7.org/02-authorsguide.html#quantities + * @param unit + * @return A CQL calendar unit if the input unit is a Ucum definite-duration unit, otherwise, the input unit + */ + public String toCqlCalendarUnit(String unit); + /** * Converts a CQL Ratio to a FHIR Ratio * diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java index 40600f108..2d2c53691 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R4FhirTypeConverter.java @@ -70,9 +70,7 @@ public IPrimitiveType toFhirDateTime(DateTime value) { return null; } - DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME; - java.util.Date date = java.util.Date.from(Instant.from(dtf.parse(value.getDateTime().toString()))); - return new DateTimeType(date); + return new DateTimeType(value.getDateTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); } @Override @@ -99,8 +97,12 @@ public ICompositeType toFhirQuantity(Quantity value) { return null; } + String unit = value.getUnit(); + String system = isCqlCalendarUnit(unit) ? "http://hl7.org/fhirpath/CodeSystem/calendar-units" : "http://unitsofmeasure.org"; + String ucumUnit = toUcumUnit(unit); + return new org.hl7.fhir.r4.model.Quantity() - .setSystem("http://unitsofmeasure.org").setCode(value.getUnit()).setValue(value.getValue()); + .setSystem(system).setCode(ucumUnit).setValue(value.getValue()).setUnit(unit); } @Override @@ -163,15 +165,17 @@ public ICompositeType toFhirPeriod(Interval value) { Period period = new Period(); if (getSimpleName(value.getPointType().getTypeName()).equals("DateTime")) { if (value.getStart() != null) { - period.setStart(toFhirDateTime((DateTime) value.getStart()).getValue()); + period.setStartElement((DateTimeType)toFhirDateTime((DateTime)value.getStart())); } if (value.getEnd() != null) { - period.setEnd(toFhirDateTime((DateTime) value.getEnd()).getValue()); + period.setEndElement((DateTimeType)toFhirDateTime((DateTime)value.getEnd())); } return period; } else if (getSimpleName(value.getPointType().getTypeName()).equals("Date")) { + // TODO: This will construct DateTimeType values in FHIR with the system timezone id, not the + // timezoneoffset of the evaluation request..... this is a bug waiting to happen if (value.getStart() != null) { period.setStart(toFhirDate((Date) value.getStart()).getValue()); } @@ -223,7 +227,10 @@ public Quantity toCqlQuantity(ICompositeType value) { } org.hl7.fhir.r4.model.Quantity quantity = (org.hl7.fhir.r4.model.Quantity) value; - return new Quantity().withUnit(quantity.getUnit()).withValue(quantity.getValue()); + if (quantity.hasComparator()) { + throw new IllegalArgumentException("Cannot convert a FHIR Quantity with a comparator to a CQL quantity"); + } + return new Quantity().withUnit(toCqlCalendarUnit(quantity.getUnit())).withValue(quantity.getValue()); } @Override diff --git a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java index f5452899f..8d820ce43 100644 --- a/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java +++ b/Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/R5FhirTypeConverter.java @@ -2,7 +2,9 @@ import java.math.BigDecimal; import java.time.Instant; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; import java.util.stream.Collectors; import org.hl7.fhir.r5.model.*; @@ -70,9 +72,7 @@ public IPrimitiveType toFhirDateTime(DateTime value) { return null; } - DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME; - java.util.Date date = java.util.Date.from(Instant.from(dtf.parse(value.getDateTime().toString()))); - return new DateTimeType(date); + return new DateTimeType(value.getDateTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); } @Override @@ -99,8 +99,12 @@ public ICompositeType toFhirQuantity(Quantity value) { return null; } + String unit = value.getUnit(); + String system = isCqlCalendarUnit(unit) ? "http://hl7.org/fhirpath/CodeSystem/calendar-units" : "http://unitsofmeasure.org"; + String ucumUnit = toUcumUnit(unit); + return new org.hl7.fhir.r5.model.Quantity() - .setSystem("http://unitsofmeasure.org").setCode(value.getUnit()).setValue(value.getValue()); + .setSystem(system).setCode(ucumUnit).setValue(value.getValue()).setUnit(unit); } @Override @@ -163,15 +167,18 @@ public ICompositeType toFhirPeriod(Interval value) { Period period = new Period(); if (getSimpleName(value.getPointType().getTypeName()).equals("DateTime")) { if (value.getStart() != null) { - period.setStart(toFhirDateTime((DateTime) value.getStart()).getValue()); + period.setStartElement((DateTimeType)toFhirDateTime((DateTime)value.getStart())); } if (value.getEnd() != null) { - period.setEnd(toFhirDateTime((DateTime) value.getEnd()).getValue()); + period.setEndElement((DateTimeType)toFhirDateTime((DateTime)value.getEnd())); } return period; - } else if (getSimpleName(value.getPointType().getTypeName()).equals("Date")) { + } + else if (getSimpleName(value.getPointType().getTypeName()).equals("Date")) { + // TODO: This will construct DateTimeType values in FHIR with the system timezone id, not the + // timezoneoffset of the evaluation request..... this is a bug waiting to happen if (value.getStart() != null) { period.setStart(toFhirDate((Date) value.getStart()).getValue()); } @@ -223,7 +230,10 @@ public Quantity toCqlQuantity(ICompositeType value) { } org.hl7.fhir.r5.model.Quantity quantity = (org.hl7.fhir.r5.model.Quantity) value; - return new Quantity().withUnit(quantity.getUnit()).withValue(quantity.getValue()); + if (quantity.hasComparator()) { + throw new IllegalArgumentException("Cannot convert a FHIR Quantity with a comparator to a CQL quantity"); + } + return new Quantity().withUnit(toCqlCalendarUnit(quantity.getUnit())).withValue(quantity.getValue()); } @Override diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java index ac919f0ca..d529f4d74 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu2TypeConverterTests.java @@ -258,7 +258,7 @@ public void TestDateTimeToFhirDateTime() { @Test public void TestQuantityToFhirQuantity() { org.hl7.fhir.dstu2.model.Quantity expected = new org.hl7.fhir.dstu2.model.Quantity().setValue(new BigDecimal("2.0")).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.dstu2.model.Quantity actual = (org.hl7.fhir.dstu2.model.Quantity) this.typeConverter .toFhirQuantity(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml")); assertTrue(expected.equalsDeep(actual)); @@ -267,9 +267,9 @@ public void TestQuantityToFhirQuantity() { @Test public void TestRatioToFhirRatio() { org.hl7.fhir.dstu2.model.Quantity expectedNumerator = new org.hl7.fhir.dstu2.model.Quantity().setValue(new BigDecimal("1.0")).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.dstu2.model.Quantity expectedDenominator = new org.hl7.fhir.dstu2.model.Quantity().setValue(new BigDecimal("2.0")).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.dstu2.model.Ratio expected = new org.hl7.fhir.dstu2.model.Ratio().setNumerator(expectedNumerator) .setDenominator(expectedDenominator); diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java index dfd1336e1..13dd8e4ff 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/Dstu3TypeConverterTests.java @@ -258,7 +258,7 @@ public void TestDateTimeToFhirDateTime() { @Test public void TestQuantityToFhirQuantity() { org.hl7.fhir.dstu3.model.Quantity expected = new org.hl7.fhir.dstu3.model.Quantity(2.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.dstu3.model.Quantity actual = (org.hl7.fhir.dstu3.model.Quantity) this.typeConverter .toFhirQuantity(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml")); assertTrue(expected.equalsDeep(actual)); @@ -267,9 +267,9 @@ public void TestQuantityToFhirQuantity() { @Test public void TestRatioToFhirRatio() { org.hl7.fhir.dstu3.model.Quantity expectedNumerator = new org.hl7.fhir.dstu3.model.Quantity(1.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.dstu3.model.Quantity expectedDenominator = new org.hl7.fhir.dstu3.model.Quantity(2.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.dstu3.model.Ratio expected = new org.hl7.fhir.dstu3.model.Ratio().setNumerator(expectedNumerator) .setDenominator(expectedDenominator); @@ -344,8 +344,8 @@ public void TestInvalidIntervalToFhirPeriod() { @Test public void TestIntervalToFhirRange() { Range expected = new Range() - .setLow((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(2.0).setCode("ml").setSystem("http://unitsofmeasure.org")) - .setHigh((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(5.0).setCode("ml").setSystem("http://unitsofmeasure.org")); + .setLow((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(2.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")) + .setHigh((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(5.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")); Range actual = (Range) this.typeConverter .toFhirRange(new Interval(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml"), true, new Quantity().withValue(new BigDecimal("5.0")).withUnit("ml"), true)); @@ -369,8 +369,8 @@ public void TestIntervalToFhirInterval() { assertTrue(expectedPeriod.equalsDeep(actualPeriod)); Range expectedRange = new Range() - .setLow((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(2.0).setCode("ml").setSystem("http://unitsofmeasure.org")) - .setHigh((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(5.0).setCode("ml").setSystem("http://unitsofmeasure.org")); + .setLow((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(2.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")) + .setHigh((SimpleQuantity)new org.hl7.fhir.dstu3.model.SimpleQuantity().setValue(5.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")); Range actualRange = (Range) this.typeConverter .toFhirInterval(new Interval(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml"), true, new Quantity().withValue(new BigDecimal("5.0")).withUnit("ml"), true)); diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java index c9ecfd1d4..41304496e 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R4TypeConverterTests.java @@ -257,7 +257,7 @@ public void TestDateTimeToFhirDateTime() { @Test public void TestQuantityToFhirQuantity() { org.hl7.fhir.r4.model.Quantity expected = new org.hl7.fhir.r4.model.Quantity(2.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.r4.model.Quantity actual = (org.hl7.fhir.r4.model.Quantity) this.typeConverter .toFhirQuantity(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml")); assertTrue(expected.equalsDeep(actual)); @@ -266,9 +266,9 @@ public void TestQuantityToFhirQuantity() { @Test public void TestRatioToFhirRatio() { org.hl7.fhir.r4.model.Quantity expectedNumerator = new org.hl7.fhir.r4.model.Quantity(1.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.r4.model.Quantity expectedDenominator = new org.hl7.fhir.r4.model.Quantity(2.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.r4.model.Ratio expected = new org.hl7.fhir.r4.model.Ratio().setNumerator(expectedNumerator) .setDenominator(expectedDenominator); @@ -343,8 +343,8 @@ public void TestInvalidIntervalToFhirPeriod() { @Test public void TestIntervalToFhirRange() { Range expected = new Range() - .setLow(new org.hl7.fhir.r4.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org")) - .setHigh(new org.hl7.fhir.r4.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org")); + .setLow(new org.hl7.fhir.r4.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")) + .setHigh(new org.hl7.fhir.r4.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")); Range actual = (Range) this.typeConverter .toFhirRange(new Interval(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml"), true, new Quantity().withValue(new BigDecimal("5.0")).withUnit("ml"), true)); @@ -368,8 +368,8 @@ public void TestIntervalToFhirInterval() { assertTrue(expectedPeriod.equalsDeep(actualPeriod)); Range expectedRange = new Range() - .setLow(new org.hl7.fhir.r4.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org")) - .setHigh(new org.hl7.fhir.r4.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org")); + .setLow(new org.hl7.fhir.r4.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")) + .setHigh(new org.hl7.fhir.r4.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")); Range actualRange = (Range) this.typeConverter .toFhirInterval(new Interval(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml"), true, new Quantity().withValue(new BigDecimal("5.0")).withUnit("ml"), true)); diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java index d01493064..fe2501c21 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/converter/R5TypeConverterTests.java @@ -257,7 +257,7 @@ public void TestDateTimeToFhirDateTime() { @Test public void TestQuantityToFhirQuantity() { org.hl7.fhir.r5.model.Quantity expected = new org.hl7.fhir.r5.model.Quantity(2.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.r5.model.Quantity actual = (org.hl7.fhir.r5.model.Quantity) this.typeConverter .toFhirQuantity(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml")); assertTrue(expected.equalsDeep(actual)); @@ -266,9 +266,9 @@ public void TestQuantityToFhirQuantity() { @Test public void TestRatioToFhirRatio() { org.hl7.fhir.r5.model.Quantity expectedNumerator = new org.hl7.fhir.r5.model.Quantity(1.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.r5.model.Quantity expectedDenominator = new org.hl7.fhir.r5.model.Quantity(2.0).setCode("ml") - .setSystem("http://unitsofmeasure.org"); + .setSystem("http://unitsofmeasure.org").setUnit("ml"); org.hl7.fhir.r5.model.Ratio expected = new org.hl7.fhir.r5.model.Ratio().setNumerator(expectedNumerator) .setDenominator(expectedDenominator); @@ -343,8 +343,8 @@ public void TestInvalidIntervalToFhirPeriod() { @Test public void TestIntervalToFhirRange() { Range expected = new Range() - .setLow(new org.hl7.fhir.r5.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org")) - .setHigh(new org.hl7.fhir.r5.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org")); + .setLow(new org.hl7.fhir.r5.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")) + .setHigh(new org.hl7.fhir.r5.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")); Range actual = (Range) this.typeConverter .toFhirRange(new Interval(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml"), true, new Quantity().withValue(new BigDecimal("5.0")).withUnit("ml"), true)); @@ -368,8 +368,8 @@ public void TestIntervalToFhirInterval() { assertTrue(expectedPeriod.equalsDeep(actualPeriod)); Range expectedRange = new Range() - .setLow(new org.hl7.fhir.r5.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org")) - .setHigh(new org.hl7.fhir.r5.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org")); + .setLow(new org.hl7.fhir.r5.model.Quantity(2.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")) + .setHigh(new org.hl7.fhir.r5.model.Quantity(5.0).setCode("ml").setSystem("http://unitsofmeasure.org").setUnit("ml")); Range actualRange = (Range) this.typeConverter .toFhirInterval(new Interval(new Quantity().withValue(new BigDecimal("2.0")).withUnit("ml"), true, new Quantity().withValue(new BigDecimal("5.0")).withUnit("ml"), true)); diff --git a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml index 2b0e5e6f1..ff1a881dc 100644 --- a/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml +++ b/Src/java/engine-fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml @@ -321,7 +321,7 @@ minimum DateTime - @0001-01-01T00:00:00.000 + @0001-01-01T00:00:00.000Z minimum Date @@ -348,7 +348,7 @@ maximum DateTime - @9999-12-31T23:59:59.999 + @9999-12-31T23:59:59.999Z maximum Date diff --git a/Src/java/engine-jackson/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java b/Src/java/engine-jackson/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java index 036b7ffea..6296f01cc 100644 --- a/Src/java/engine-jackson/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java +++ b/Src/java/engine-jackson/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java @@ -379,7 +379,7 @@ public void testMaximum() { Assert.assertEquals(((BigDecimal) result).compareTo(new BigDecimal("9999999999999999999999999999.99999999")), 0); result = context.resolveExpressionRef("DateTimeMaxValue").getExpression().evaluate(context); - Assert.assertTrue(EquivalentEvaluator.equivalent(result, new DateTime(null, 9999, 12, 31, 23, 59, 59, 999))); + Assert.assertTrue(EquivalentEvaluator.equivalent(result, new DateTime(BigDecimal.ZERO, 9999, 12, 31, 23, 59, 59, 999))); result = context.resolveExpressionRef("TimeMaxValue").getExpression().evaluate(context); Assert.assertTrue(EquivalentEvaluator.equivalent(result, new Time(23, 59, 59, 999))); @@ -401,7 +401,7 @@ public void testMinimum() { Assert.assertTrue(((BigDecimal) result).compareTo(new BigDecimal("-9999999999999999999999999999.99999999")) == 0); result = context.resolveExpressionRef("DateTimeMinValue").getExpression().evaluate(context); - Assert.assertTrue(EquivalentEvaluator.equivalent(result, new DateTime(null, 1, 1, 1, 0, 0, 0, 0))); + Assert.assertTrue(EquivalentEvaluator.equivalent(result, new DateTime(BigDecimal.ZERO, 1, 1, 1, 0, 0, 0, 0))); result = context.resolveExpressionRef("TimeMinValue").getExpression().evaluate(context); Assert.assertTrue(EquivalentEvaluator.equivalent(result, new Time(0, 0, 0, 0))); diff --git a/Src/java/engine-jackson/src/test/resources/org/opencds/cqf/cql/engine/execution/portable/CqlTestSuite.cql b/Src/java/engine-jackson/src/test/resources/org/opencds/cqf/cql/engine/execution/portable/CqlTestSuite.cql index 15ce5faad..60b333067 100644 --- a/Src/java/engine-jackson/src/test/resources/org/opencds/cqf/cql/engine/execution/portable/CqlTestSuite.cql +++ b/Src/java/engine-jackson/src/test/resources/org/opencds/cqf/cql/engine/execution/portable/CqlTestSuite.cql @@ -484,7 +484,7 @@ define MinTime: minimum Time define test_MinInteger: TestMessage(MinInteger = -2147483648, 'MinInteger', toString(-2147483648), toString(MinInteger)) define test_MinDecimal: TestMessage(MinDecimal = -9999999999999999999999999999.99999999, 'MinDecimal', toString(-9999999999999999999999999999.99999999), toString(MinDecimal)) define test_MinDate: TestMessage(MinDate = @0001-01-01, 'MinDate', toString(@0001-01-01), toString(MinDate)) -define test_MinDateTime: TestMessage(MinDateTime = @0001-01-01T00:00:00.000, 'MinDateTime', toString(@0001-01-01T00:00:00.000), toString(MinDateTime)) +define test_MinDateTime: TestMessage(MinDateTime = @0001-01-01T00:00:00.000Z, 'MinDateTime', toString(@0001-01-01T00:00:00.000Z), toString(MinDateTime)) define test_MinTime: TestMessage(MinTime = @T00:00:00.000, 'MinTime', toString(@T00:00:00.000), toString(MinTime)) // MaxValue @@ -497,7 +497,7 @@ define MaxTime: maximum Time define test_MaxInteger: TestMessage(MaxInteger = 2147483647, 'MaxInteger', toString(2147483647), toString(MaxInteger)) define test_MaxDecimal: TestMessage(MaxDecimal = 9999999999999999999999999999.99999999, 'MaxDecimal', toString(9999999999999999999999999999.99999999), toString(MaxDecimal)) define test_MaxDate: TestMessage(MaxDate = @9999-12-31, 'MaxDate', toString(@9999-12-31), toString(MaxDate)) -define test_MaxDateTime: TestMessage(MaxDateTime = @9999-12-31T23:59:59.999, 'MaxDateTime', toString(@9999-12-31T23:59:59.999), toString(MaxDateTime)) +define test_MaxDateTime: TestMessage(MaxDateTime = @9999-12-31T23:59:59.999Z, 'MaxDateTime', toString(@9999-12-31T23:59:59.999Z), toString(MaxDateTime)) define test_MaxTime: TestMessage(MaxTime = @T23:59:59.999, 'MaxTime', toString(@T23:59:59.999), toString(MaxTime)) // TruncatedDivide @@ -3811,7 +3811,7 @@ define DateTimeNullEndCollapseNoOverlapExpected: { DateTime1_2Interval, DateTime define DateTimeNullStartCollapseNoOverlapExpected: { DateTimeNull_5Interval, DateTime9_10Interval } define DateTimeNullEndCollapseExpected: { Interval[DateTime(2012, 1, 1, 0, 0, 0, 0), null] } // TODO: should be { Interval[(null as DateTime), (null as DateTime)] } but that currently evaluates to null in the engine -define DateTimeNullStartEndCollapseExpected: { Interval[null, DateTime(9999, 12, 31, 23, 59, 59, 999)] } +define DateTimeNullStartEndCollapseExpected: { Interval[null, DateTime(9999, 12, 31, 23, 59, 59, 999, 0.0)] } define QuantityMeterNullLowIntervalList: { Interval[null, ToQuantity('1.995 \'m\'')], Interval[ToQuantity('2 \'m\''), ToQuantity('3 \'m\'')] } define CollapseQuantityNullLowUnitsWithinPerExpected : { Interval[null, ToQuantity('3 \'m\'')] } define QuantityMeterNullHighIntervalList: { Interval[ToQuantity('1 \'m\''),ToQuantity('1.995 \'m\'')], Interval[ToQuantity('2 \'m\''), null] } diff --git a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MaxValueEvaluator.java b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MaxValueEvaluator.java index 2cb88f1b6..682351158 100644 --- a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MaxValueEvaluator.java +++ b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MaxValueEvaluator.java @@ -10,6 +10,8 @@ import org.opencds.cqf.cql.engine.runtime.Time; import org.opencds.cqf.cql.engine.runtime.Value; +import java.math.BigDecimal; + /* maximum() T @@ -43,7 +45,7 @@ public static Object maxValue(String type) { return new Date(9999, 12, 31); } if (type.endsWith("DateTime")) { - return new DateTime(null, 9999, 12, 31, 23, 59, 59, 999); + return new DateTime(BigDecimal.ZERO, 9999, 12, 31, 23, 59, 59, 999); } if (type.endsWith("Time")) { return new Time(23, 59, 59, 999); diff --git a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MinValueEvaluator.java b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MinValueEvaluator.java index d53004b31..69644ad09 100644 --- a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MinValueEvaluator.java +++ b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MinValueEvaluator.java @@ -10,6 +10,8 @@ import org.opencds.cqf.cql.engine.runtime.Time; import org.opencds.cqf.cql.engine.runtime.Value; +import java.math.BigDecimal; + /* minimum() T @@ -43,7 +45,7 @@ public static Object minValue(String type) { return new Date(1, 1, 1); } if (type.endsWith("DateTime")) { - return new DateTime(null,1, 1, 1, 0, 0, 0, 0); + return new DateTime(BigDecimal.ZERO,1, 1, 1, 0, 0, 0, 0); } if (type.endsWith("Time")) { return new Time(0, 0, 0, 0); diff --git a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/ToDateTimeEvaluator.java b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/ToDateTimeEvaluator.java index 3dff4d8f1..28b2dc6a8 100644 --- a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/ToDateTimeEvaluator.java +++ b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/ToDateTimeEvaluator.java @@ -6,6 +6,7 @@ import org.opencds.cqf.cql.engine.execution.Context; import org.opencds.cqf.cql.engine.runtime.Date; import org.opencds.cqf.cql.engine.runtime.DateTime; +import org.opencds.cqf.cql.engine.runtime.TemporalHelper; /* @@ -60,7 +61,11 @@ protected Object internalEvaluate(Context context) { } if (operand instanceof Date) { - return new DateTime(null, ((Date) operand).getDate().getYear(), ((Date) operand).getDate().getMonthValue(), ((Date) operand).getDate().getDayOfMonth(), 0, 0, 0, 0); + return new DateTime(TemporalHelper.zoneToOffset(context.getEvaluationOffsetDateTime().getOffset()), + ((Date) operand).getDate().getYear(), + ((Date) operand).getDate().getMonthValue(), + ((Date) operand).getDate().getDayOfMonth(), + 0, 0, 0, 0); } throw new InvalidOperatorArgument( diff --git a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/Context.java b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/Context.java index 2502220e0..ad605545c 100644 --- a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/Context.java +++ b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/Context.java @@ -644,6 +644,14 @@ public void setParameter(String libraryName, String name, Object value) { } } + public void setParameters(Library library, Map parameters) { + if (parameters != null) { + for (Map.Entry parameterValue : parameters.entrySet()) { + setParameter(null, parameterValue.getKey(), parameterValue.getValue()); + } + } + } + public Object resolveParameterRef(String libraryName, String name) { boolean enteredLibrary = enterLibrary(libraryName); try { diff --git a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/CqlEngine.java b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/CqlEngine.java index e749b9d08..6d0bee2bc 100644 --- a/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/CqlEngine.java +++ b/Src/java/engine/src/main/java/org/opencds/cqf/cql/engine/execution/CqlEngine.java @@ -204,20 +204,7 @@ private void setParametersForContext(Library library, Context context, Pair parameterValue : parameters.entrySet()) { - context.setParameter(library.getLocalId(), parameterValue.getKey(), parameterValue.getValue()); - } - - if (library.getIncludes() != null && library.getIncludes().getDef() != null) { - for (IncludeDef def : library.getIncludes().getDef()) { - String name = def.getLocalIdentifier(); - for (Map.Entry parameterValue : parameters.entrySet()) { - context.setParameter(name, parameterValue.getKey(), parameterValue.getValue()); - } - } - } - } + context.setParameters(library, parameters); } private Context initializeContext(Map libraryCache, Library library, DebugMap debugMap, ZonedDateTime evaluationDateTime) {