From 31160ea5799a7d6ad8acc6bb7c5aee4f3a38648d Mon Sep 17 00:00:00 2001 From: JP Date: Wed, 11 Dec 2024 10:40:19 -0700 Subject: [PATCH] Convert the `model` module to Kotlin (#1465) * ChoiceType * ClassTypeElement * InstantiationContext * TupleType, TupleTypeelement, SearchType, Relationship * NamespaceInfo * ModelContext * ModelIdentifier * ModelInfoProvider, NamespaceAware, ModelInfoReader, ModelInfoReaderFactory, ModelInfoReaderProvider * small clean up * NamespaceManager, SystemModelInfoProvider * clean up * NamedType, InvalidRedeclarationException * Break out DataType interface * DataType * BaseDataType * Little bit of cleanup for choice types * Fixing Locale for string format * ClassType * ProfileType * ListType * IntervalType * TypeParameter * TupleType, ClassType * Bug fixes for classes and tuples * GenericClassSignatureParser * More cleanup of GenericClassSignatureParser * Remove unused assertion --- ...otlin-multiplatform-conventions.gradle.kts | 3 + .../fhir/npm/NpmModelInfoProvider.java | 6 +- .../fhir/npm/NpmPackageManagerTests.java | 7 +- .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 216 +++++--- .../cqframework/cql/cql2elm/CqlCompiler.kt | 3 +- .../cql/cql2elm/CqlTranslatorOptionsMapper.kt | 25 +- .../org/cqframework/cql/cql2elm/DataTypes.kt | 5 +- .../cql/cql2elm/DefaultLibrarySourceLoader.kt | 35 +- .../cql2elm/DefaultLibrarySourceProvider.kt | 21 +- .../cql/cql2elm/DefaultModelInfoProvider.kt | 15 +- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 233 +++++---- .../cqframework/cql/cql2elm/LibraryManager.kt | 14 +- .../cql/cql2elm/LibraryReaderUtil.kt | 8 +- .../cql/cql2elm/LibrarySourceLoader.kt | 8 +- .../cql/cql2elm/ModelInfoLoader.kt | 37 +- .../cqframework/cql/cql2elm/ModelManager.kt | 56 +-- .../org/cqframework/cql/cql2elm/PathAware.kt | 2 +- .../cql2elm/PriorityLibrarySourceLoader.kt | 40 +- .../cql2elm/StringLibrarySourceProvider.kt | 4 +- .../cql/cql2elm/SystemFunctionResolver.kt | 97 ++-- .../cql/cql2elm/SystemMethodResolver.kt | 14 +- .../cqframework/cql/cql2elm/TypeBuilder.kt | 5 +- .../cql2elm/model/InstantiationContextImpl.kt | 6 +- .../cqframework/cql/cql2elm/model/Model.kt | 6 +- .../cql/cql2elm/model/ModelImporter.kt | 120 ++--- .../model/ResolvedIdentifierContext.kt | 1 + .../cql2elm/preprocessor/CqlPreprocessor.kt | 5 +- .../CqlPreprocessorElmCommonVisitor.kt | 32 +- .../cql/elm/requirements/TypeResolver.java | 4 +- .../fhir/DataRequirementsProcessorTest.java | 8 +- .../Library-BSElements-data-requirements.json | 10 +- .../fhir/model/TestDstu2ModelResolver.java | 2 +- .../fhir/model/TestDstu3ModelResolver.java | 2 +- .../fhir/model/TestR4ModelResolver.java | 4 +- Src/java/model/build.gradle.kts | 4 - .../java/org/hl7/cql/model/BaseDataType.kt | 62 +++ .../java/org/hl7/cql/model/ChoiceType.java | 168 ------- .../main/java/org/hl7/cql/model/ChoiceType.kt | 63 +++ .../java/org/hl7/cql/model/ClassType.java | 473 ------------------ .../main/java/org/hl7/cql/model/ClassType.kt | 298 +++++++++++ .../org/hl7/cql/model/ClassTypeElement.java | 110 ---- .../org/hl7/cql/model/ClassTypeElement.kt | 36 ++ .../main/java/org/hl7/cql/model/DataType.java | 87 ---- .../main/java/org/hl7/cql/model/DataType.kt | 28 ++ .../model/GenericClassSignatureParser.java | 393 --------------- .../cql/model/GenericClassSignatureParser.kt | 305 +++++++++++ .../hl7/cql/model/InstantiationContext.java | 13 - .../org/hl7/cql/model/InstantiationContext.kt | 13 + .../java/org/hl7/cql/model/IntervalType.java | 105 ---- .../java/org/hl7/cql/model/IntervalType.kt | 60 +++ .../model/InvalidRedeclarationException.java | 17 - .../model/InvalidRedeclarationException.kt | 24 + .../main/java/org/hl7/cql/model/ListType.java | 105 ---- .../main/java/org/hl7/cql/model/ListType.kt | 59 +++ .../java/org/hl7/cql/model/ModelContext.java | 43 -- .../java/org/hl7/cql/model/ModelContext.kt | 9 + .../org/hl7/cql/model/ModelIdentifier.java | 236 --------- .../java/org/hl7/cql/model/ModelIdentifier.kt | 18 + .../org/hl7/cql/model/ModelInfoProvider.java | 7 - .../org/hl7/cql/model/ModelInfoProvider.kt | 7 + .../java/org/hl7/cql/model/NamedType.java | 11 - .../main/java/org/hl7/cql/model/NamedType.kt | 8 + .../org/hl7/cql/model/NamespaceAware.java | 5 - .../java/org/hl7/cql/model/NamespaceAware.kt | 5 + .../java/org/hl7/cql/model/NamespaceInfo.java | 48 -- .../java/org/hl7/cql/model/NamespaceInfo.kt | 13 + .../org/hl7/cql/model/NamespaceManager.java | 109 ---- .../org/hl7/cql/model/NamespaceManager.kt | 74 +++ .../java/org/hl7/cql/model/ProfileType.java | 42 -- .../java/org/hl7/cql/model/ProfileType.kt | 31 ++ .../java/org/hl7/cql/model/Relationship.java | 26 - .../java/org/hl7/cql/model/Relationship.kt | 4 + .../java/org/hl7/cql/model/SearchType.java | 35 -- .../main/java/org/hl7/cql/model/SearchType.kt | 8 + .../java/org/hl7/cql/model/SimpleType.java | 112 ----- .../main/java/org/hl7/cql/model/SimpleType.kt | 64 +++ .../cql/model/SystemModelInfoProvider.java | 37 -- .../hl7/cql/model/SystemModelInfoProvider.kt | 30 ++ .../java/org/hl7/cql/model/TupleType.java | 216 -------- .../main/java/org/hl7/cql/model/TupleType.kt | 92 ++++ .../org/hl7/cql/model/TupleTypeElement.java | 65 --- .../org/hl7/cql/model/TupleTypeElement.kt | 29 ++ .../java/org/hl7/cql/model/TypeParameter.java | 131 ----- .../java/org/hl7/cql/model/TypeParameter.kt | 68 +++ .../r1/serializing/ModelInfoReader.java | 20 - .../r1/serializing/ModelInfoReader.kt | 25 + .../serializing/ModelInfoReaderFactory.java | 38 -- .../r1/serializing/ModelInfoReaderFactory.kt | 45 ++ .../serializing/ModelInfoReaderProvider.java | 5 - .../r1/serializing/ModelInfoReaderProvider.kt | 7 + .../org/hl7/cql/model/ChoiceTypeTests.java | 10 +- .../GenericClassSignatureParserTest.java | 44 +- .../tools/xsd2modelinfo/ModelImporter.java | 6 +- 93 files changed, 2084 insertions(+), 3176 deletions(-) create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ClassType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ClassType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/DataType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/DataType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ListType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ListType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamedType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamedType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/Relationship.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/Relationship.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/SearchType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/SearchType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TupleType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TupleType.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.java create mode 100644 Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.java create mode 100644 Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.kt delete mode 100644 Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.java create mode 100644 Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.kt diff --git a/Src/java/buildSrc/src/main/kotlin/cql.kotlin-multiplatform-conventions.gradle.kts b/Src/java/buildSrc/src/main/kotlin/cql.kotlin-multiplatform-conventions.gradle.kts index 12984feac..b3f60adb7 100644 --- a/Src/java/buildSrc/src/main/kotlin/cql.kotlin-multiplatform-conventions.gradle.kts +++ b/Src/java/buildSrc/src/main/kotlin/cql.kotlin-multiplatform-conventions.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl + plugins { kotlin("multiplatform") id("com.diffplug.spotless") @@ -65,6 +67,7 @@ kotlin { // Add Kotlin/WASM compilation target. // The output is in the JS packages directory. + @OptIn(ExperimentalWasmDsl::class) wasmJs { browser { } binaries.executable() diff --git a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java index 861deffd0..ea960eb2b 100644 --- a/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java +++ b/Src/java/cqf-fhir-npm/src/main/java/org/cqframework/fhir/npm/NpmModelInfoProvider.java @@ -33,10 +33,8 @@ public ModelInfo load(ModelIdentifier modelIdentifier) { // VersionedIdentifier.version: Version of the model for (NpmPackage p : packages) { try { - var identifier = new ModelIdentifier() - .withId(modelIdentifier.getId()) - .withVersion(modelIdentifier.getVersion()) - .withSystem(modelIdentifier.getSystem()); + var identifier = new ModelIdentifier( + modelIdentifier.getId(), modelIdentifier.getSystem(), modelIdentifier.getVersion()); if (identifier.getSystem() == null) { identifier.setSystem(p.canonical()); diff --git a/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java b/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java index eecf860a0..891a0dc3a 100644 --- a/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java +++ b/Src/java/cqf-fhir-npm/src/test/java/org/cqframework/fhir/npm/NpmPackageManagerTests.java @@ -1,6 +1,7 @@ package org.cqframework.fhir.npm; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -117,13 +118,11 @@ void modelInfoProviderLocal() { .parseResource(NpmPackageManagerTests.class.getResourceAsStream("testig.xml")); ImplementationGuide ig = (ImplementationGuide) convertor.convertResource(igResource); NpmPackageManager pm = new NpmPackageManager(ig); - assertTrue(pm.getNpmList().size() >= 1); + assertFalse(pm.getNpmList().isEmpty()); LibraryLoader reader = new LibraryLoader("5.0"); NpmModelInfoProvider mp = new NpmModelInfoProvider(pm.getNpmList(), reader, this); - ModelInfo mi = mp.load(new ModelIdentifier() - .withSystem("http://hl7.org/fhir/us/qicore") - .withId("QICore")); + ModelInfo mi = mp.load(new ModelIdentifier("QICore", "http://hl7.org/fhir/us/qicore", null)); assertNotNull(mi); assertEquals("QICore", mi.getName()); } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.kt index 77679caa6..638bc2353 100755 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.kt @@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory "TooManyFunctions", "ComplexCondition", "TooGenericExceptionCaught", - "ImplicitDefaultLocale", "ReturnCount", "ThrowsCount", "MaxLineLength", @@ -123,7 +122,7 @@ class Cql2ElmVisitor( if (namespaceName != null) { val namespaceUri = libraryBuilder.resolveNamespaceUri(namespaceName, true) path = NamespaceManager.getPath(namespaceUri, unqualifiedIdentifier) - modelNamespace = NamespaceInfo(namespaceName, namespaceUri) + modelNamespace = NamespaceInfo(namespaceName, namespaceUri!!) } else { path = unqualifiedIdentifier } @@ -132,6 +131,7 @@ class Cql2ElmVisitor( else parseString(ctx.localIdentifier())!! require(localIdentifier == unqualifiedIdentifier) { String.format( + Locale.US, "Local identifiers for models must be the same as the name of the model in this release of the translator (Model %s, Called %s)", unqualifiedIdentifier, localIdentifier @@ -144,21 +144,18 @@ class Cql2ElmVisitor( return usingDef } - public override fun getModel( + override fun getModel( modelNamespace: NamespaceInfo?, modelName: String?, version: String?, localIdentifier: String ): Model { - Objects.requireNonNull(modelName, "modelName") - val modelIdentifier = ModelIdentifier().withId(modelName).withVersion(version) - if (modelNamespace != null) { - modelIdentifier.system = modelNamespace.uri - } + val modelIdentifier = + ModelIdentifier(id = modelName!!, version = version, system = modelNamespace?.uri) return libraryBuilder.getModel(modelIdentifier, localIdentifier) } - private fun getLibraryPath(namespaceName: String?, unqualifiedIdentifier: String): String { + private fun getLibraryPath(namespaceName: String?, unqualifiedIdentifier: String): String? { if (namespaceName != null) { val namespaceUri = libraryBuilder.resolveNamespaceUri(namespaceName, true) return NamespaceManager.getPath(namespaceUri, unqualifiedIdentifier) @@ -173,7 +170,7 @@ class Cql2ElmVisitor( if (identifiers.isNotEmpty()) java.lang.String.join(".", identifiers) else if (libraryBuilder.namespaceInfo != null) libraryBuilder.namespaceInfo.name else null - var path: String = getLibraryPath(namespaceName, unqualifiedIdentifier) + var path = getLibraryPath(namespaceName, unqualifiedIdentifier) var library = of.createIncludeDef() .withLocalIdentifier( @@ -230,7 +227,11 @@ class Cql2ElmVisitor( } } requireNotNull(paramType) { - String.format("Could not determine parameter type for parameter %s.", param.name) + String.format( + Locale.US, + "Could not determine parameter type for parameter %s.", + param.name + ) } param.resultType = paramType if (param.default != null) { @@ -261,6 +262,7 @@ class Cql2ElmVisitor( else -> throw IllegalArgumentException( String.format( + Locale.US, "Unknown access modifier %s.", ctx.text.lowercase(Locale.getDefault()) ) @@ -278,7 +280,7 @@ class Cql2ElmVisitor( if (libraryBuilder.isCompatibleWith("1.5")) { cs.resultType = libraryBuilder.resolveTypeName("System", "CodeSystem") } else { - cs.resultType = ListType(libraryBuilder.resolveTypeName("System", "Code")) + cs.resultType = ListType(libraryBuilder.resolveTypeName("System", "Code")!!) } libraryBuilder.addCodeSystem(cs) libraryBuilder.pushIdentifier(cs.name, cs, IdentifierScope.GLOBAL) @@ -297,7 +299,7 @@ class Cql2ElmVisitor( } requireNotNull(def) { // ERROR: - String.format("Could not resolve reference to code system %s.", name) + String.format(Locale.US, "Could not resolve reference to code system %s.", name) } return of.createCodeSystemRef() .withLibraryName(libraryName) @@ -318,7 +320,7 @@ class Cql2ElmVisitor( if (def == null) { // ERROR: throw IllegalArgumentException( - String.format("Could not resolve reference to code %s.", name) + String.format(Locale.US, "Could not resolve reference to code %s.", name) ) } return of.createCodeRef() @@ -340,6 +342,7 @@ class Cql2ElmVisitor( visit(codesystem) as CodeSystemRef? ?: throw IllegalArgumentException( String.format( + Locale.US, "Could not resolve reference to code system %s.", codesystem.text ) @@ -350,7 +353,7 @@ class Cql2ElmVisitor( if (libraryBuilder.isCompatibleWith("1.5")) { vs.resultType = libraryBuilder.resolveTypeName("System", "ValueSet") } else { - vs.resultType = ListType(libraryBuilder.resolveTypeName("System", "Code")) + vs.resultType = ListType(libraryBuilder.resolveTypeName("System", "Code")!!) } libraryBuilder.addValueSet(vs) libraryBuilder.pushIdentifier(vs.name, vs, IdentifierScope.GLOBAL) @@ -363,9 +366,7 @@ class Cql2ElmVisitor( .withAccessLevel(parseAccessModifier(ctx.accessModifier())) .withName(parseString(ctx.identifier())) .withId(parseString(ctx.codeId())) - if (ctx.codesystemIdentifier() != null) { - cd.codeSystem = visit(ctx.codesystemIdentifier()) as CodeSystemRef? - } + cd.codeSystem = visit(ctx.codesystemIdentifier()) as CodeSystemRef? if (ctx.displayClause() != null) { cd.display = parseString(ctx.displayClause()!!.STRING()) } @@ -380,10 +381,8 @@ class Cql2ElmVisitor( of.createConceptDef() .withAccessLevel(parseAccessModifier(ctx.accessModifier())) .withName(parseString(ctx.identifier())) - if (ctx.codeIdentifier() != null) { - for (ci in ctx.codeIdentifier()) { - cd.code.add(visit(ci) as CodeRef?) - } + for (ci in ctx.codeIdentifier()) { + cd.code.add(visit(ci) as CodeRef?) } if (ctx.displayClause() != null) { cd.display = parseString(ctx.displayClause()!!.STRING()) @@ -400,7 +399,7 @@ class Cql2ElmVisitor( getTypeIdentifier(qualifiers, parseString(ctx.referentialOrTypeNameIdentifier())!!) val retrievedResult = libraryBuilder.getNamedTypeSpecifierResult( - String.format("%s:%s", modelIdentifier, identifier) + String.format(Locale.US, "%s:%s", modelIdentifier, identifier) ) if (retrievedResult != null) { return if (retrievedResult.hasError()) { @@ -411,6 +410,7 @@ class Cql2ElmVisitor( libraryBuilder.resolveTypeName(modelIdentifier, identifier) ?: throw CqlCompilerException( String.format( + Locale.US, "Could not find type for model: %s and name: %s", modelIdentifier, identifier @@ -577,6 +577,7 @@ class Cql2ElmVisitor( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Identifier %s is already in use in this library.", expressionDef.name ) @@ -758,7 +759,7 @@ class Cql2ElmVisitor( } } if (listType == null) { - listType = ListType(elementType) + listType = ListType(elementType!!) } list.resultType = listType return list @@ -781,7 +782,7 @@ class Cql2ElmVisitor( var millisecond = -1 if (hour < 0 || hour > 24) { throw IllegalArgumentException( - String.format("Invalid hour in time literal (%s).", input) + String.format(Locale.US, "Invalid hour in time literal (%s).", input) ) } result.hour = libraryBuilder.createLiteral(hour) @@ -789,7 +790,7 @@ class Cql2ElmVisitor( minute = matcher.group(3).toInt() if ((minute < 0) || (minute >= 60) || (hour == 24 && minute > 0)) { throw IllegalArgumentException( - String.format("Invalid minute in time literal (%s).", input) + String.format(Locale.US, "Invalid minute in time literal (%s).", input) ) } result.minute = libraryBuilder.createLiteral(minute) @@ -798,7 +799,7 @@ class Cql2ElmVisitor( second = matcher.group(5).toInt() if ((second < 0) || (second >= 60) || (hour == 24 && second > 0)) { throw IllegalArgumentException( - String.format("Invalid second in time literal (%s).", input) + String.format(Locale.US, "Invalid second in time literal (%s).", input) ) } result.second = libraryBuilder.createLiteral(second) @@ -807,7 +808,11 @@ class Cql2ElmVisitor( millisecond = matcher.group(7).toInt() if (millisecond < 0 || (hour == 24 && millisecond > 0)) { throw IllegalArgumentException( - String.format("Invalid millisecond in time literal (%s).", input) + String.format( + Locale.US, + "Invalid millisecond in time literal (%s).", + input + ) ) } result.millisecond = libraryBuilder.createLiteral(millisecond) @@ -817,6 +822,7 @@ class Cql2ElmVisitor( } catch (e: RuntimeException) { throw IllegalArgumentException( String.format( + Locale.US, "Invalid time input (%s). Use ISO 8601 time representation (hh:mm:ss.fff).", input ), @@ -826,6 +832,7 @@ class Cql2ElmVisitor( } else { throw IllegalArgumentException( String.format( + Locale.US, "Invalid time input (%s). Use ISO 8601 time representation (hh:mm:ss.fff).", input ) @@ -905,7 +912,11 @@ class Cql2ElmVisitor( month = matcher.group(5).toInt() if (month < 0 || month > 12) { throw IllegalArgumentException( - String.format("Invalid month in date/time literal (%s).", input) + String.format( + Locale.US, + "Invalid month in date/time literal (%s).", + input + ) ) } result.month = libraryBuilder.createLiteral(month) @@ -923,7 +934,11 @@ class Cql2ElmVisitor( } if (day < 0 || day > maxDay) { throw IllegalArgumentException( - String.format("Invalid day in date/time literal (%s).", input) + String.format( + Locale.US, + "Invalid day in date/time literal (%s).", + input + ) ) } result.day = libraryBuilder.createLiteral(day) @@ -932,7 +947,11 @@ class Cql2ElmVisitor( hour = matcher.group(13).toInt() if (hour < 0 || hour > 24) { throw IllegalArgumentException( - String.format("Invalid hour in date/time literal (%s).", input) + String.format( + Locale.US, + "Invalid hour in date/time literal (%s).", + input + ) ) } result.hour = libraryBuilder.createLiteral(hour) @@ -941,7 +960,11 @@ class Cql2ElmVisitor( minute = matcher.group(15).toInt() if ((minute < 0) || (minute >= 60) || (hour == 24 && minute > 0)) { throw IllegalArgumentException( - String.format("Invalid minute in date/time literal (%s).", input) + String.format( + Locale.US, + "Invalid minute in date/time literal (%s).", + input + ) ) } result.minute = libraryBuilder.createLiteral(minute) @@ -950,7 +973,11 @@ class Cql2ElmVisitor( second = matcher.group(17).toInt() if ((second < 0) || (second >= 60) || (hour == 24 && second > 0)) { throw IllegalArgumentException( - String.format("Invalid second in date/time literal (%s).", input) + String.format( + Locale.US, + "Invalid second in date/time literal (%s).", + input + ) ) } result.second = libraryBuilder.createLiteral(second) @@ -959,7 +986,11 @@ class Cql2ElmVisitor( millisecond = matcher.group(19).toInt() if (millisecond < 0 || (hour == 24 && millisecond > 0)) { throw IllegalArgumentException( - String.format("Invalid millisecond in date/time literal (%s).", input) + String.format( + Locale.US, + "Invalid millisecond in date/time literal (%s).", + input + ) ) } result.millisecond = libraryBuilder.createLiteral(millisecond) @@ -974,6 +1005,7 @@ class Cql2ElmVisitor( if (hourOffset < 0 || hourOffset > 14) { throw IllegalArgumentException( String.format( + Locale.US, "Timezone hour offset is out of range in date/time literal (%s).", input ) @@ -987,6 +1019,7 @@ class Cql2ElmVisitor( ) { throw IllegalArgumentException( String.format( + Locale.US, "Timezone minute offset is out of range in date/time literal (%s).", input ) @@ -1002,6 +1035,7 @@ class Cql2ElmVisitor( if (hourOffset < 0 || hourOffset > 14) { throw IllegalArgumentException( String.format( + Locale.US, "Timezone hour offset is out of range in date/time literal (%s).", input ) @@ -1032,6 +1066,7 @@ class Cql2ElmVisitor( } catch (e: RuntimeException) { throw IllegalArgumentException( String.format( + Locale.US, "Invalid date-time input (%s). Use ISO 8601 date time representation (yyyy-MM-ddThh:mm:ss.fff(Z|(+/-hh:mm)).", input ), @@ -1041,6 +1076,7 @@ class Cql2ElmVisitor( } else { throw IllegalArgumentException( String.format( + Locale.US, "Invalid date-time input (%s). Use ISO 8601 date time representation (yyyy-MM-ddThh:mm:ss.fff(Z|+/-hh:mm)).", input ) @@ -1091,7 +1127,7 @@ class Cql2ElmVisitor( BigDecimal(value) } catch (@Suppress("SwallowedException") e: Exception) { throw IllegalArgumentException( - String.format("Could not parse number literal: %s", value) + String.format(Locale.US, "Could not parse number literal: %s", value) ) } } @@ -1158,7 +1194,7 @@ class Cql2ElmVisitor( } else -> throw IllegalArgumentException( - String.format("Unsupported operator: %s.", ctx.getChild(1)!!.text) + String.format(Locale.US, "Unsupported operator: %s.", ctx.getChild(1)!!.text) ) } exp!!.withOperand( @@ -1207,7 +1243,7 @@ class Cql2ElmVisitor( } else -> throw IllegalArgumentException( - String.format("Unsupported operator: %s.", ctx.getChild(1)!!.text) + String.format(Locale.US, "Unsupported operator: %s.", ctx.getChild(1)!!.text) ) } if (exp is BinaryExpression) { @@ -1275,7 +1311,10 @@ class Cql2ElmVisitor( "maximum" -> { libraryBuilder.buildMaximum(targetType!!.resultType) } - else -> throw IllegalArgumentException(String.format("Unknown extent: %s", extent)) + else -> + throw IllegalArgumentException( + String.format(Locale.US, "Unknown extent: %s", extent) + ) } } @@ -1343,7 +1382,7 @@ class Cql2ElmVisitor( "milliseconds" -> DateTimePrecision.MILLISECOND else -> throw IllegalArgumentException( - String.format("Unknown precision '%s'.", dateTimePrecision) + String.format(Locale.US, "Unknown precision '%s'.", dateTimePrecision) ) } } @@ -1389,7 +1428,9 @@ class Cql2ElmVisitor( "week" -> throw IllegalArgumentException("Date/time values do not have a week component.") else -> - throw IllegalArgumentException(String.format("Unknown precision '%s'.", component)) + throw IllegalArgumentException( + String.format(Locale.US, "Unknown precision '%s'.", component) + ) } libraryBuilder.resolveUnaryCall("System", operatorName, result!!) return result @@ -1580,7 +1621,7 @@ class Cql2ElmVisitor( return contains } } - throw IllegalArgumentException(String.format("Unknown operator: %s", operator)) + throw IllegalArgumentException(String.format(Locale.US, "Unknown operator: %s", operator)) } override fun visitAndExpression(ctx: AndExpressionContext): And? { @@ -1691,7 +1732,7 @@ class Cql2ElmVisitor( } else -> throw IllegalArgumentException( - String.format("Unknown operator: %s", ctx.getChild(1)!!.text) + String.format(Locale.US, "Unknown operator: %s", ctx.getChild(1)!!.text) ) } exp.withOperand(parseExpression(ctx.expression(0)), parseExpression(ctx.expression(1))) @@ -1749,6 +1790,7 @@ class Cql2ElmVisitor( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Identifier %s is a library and cannot be used as an expression.", result.libraryName ) @@ -1799,6 +1841,7 @@ class Cql2ElmVisitor( ?: // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not resolve conversion from type %s to type %s.", operand.resultType, targetType.resultType @@ -1876,7 +1919,7 @@ class Cql2ElmVisitor( } else -> throw IllegalArgumentException( - String.format("Unknown boolean test predicate %s.", lastChild) + String.format(Locale.US, "Unknown boolean test predicate %s.", lastChild) ) } if ("not" == nextToLast) { @@ -1978,6 +2021,7 @@ class Cql2ElmVisitor( else -> throw IllegalArgumentException( String.format( + Locale.US, "Unknown relative qualifier: '%s'.", ctx.relativeQualifier()!!.text ) @@ -2802,7 +2846,7 @@ class Cql2ElmVisitor( } } throw IllegalArgumentException( - String.format("Unknown aggregate operator %s.", ctx.getChild(0)!!.text) + String.format(Locale.US, "Unknown aggregate operator %s.", ctx.getChild(0)!!.text) ) } @@ -2858,7 +2902,7 @@ class Cql2ElmVisitor( } } throw IllegalArgumentException( - String.format("Unknown aggregate set operator %s.", ctx.getChild(0)!!.text) + String.format(Locale.US, "Unknown aggregate set operator %s.", ctx.getChild(0)!!.text) ) } @@ -2875,12 +2919,16 @@ class Cql2ElmVisitor( libraryBuilder.resolveTypeName(model, label) ?: // ERROR: throw IllegalArgumentException( - String.format("Could not resolve type name %s.", label) + String.format(Locale.US, "Could not resolve type name %s.", label) ) if (dataType !is ClassType || !dataType.isRetrievable) { // ERROR: throw IllegalArgumentException( - String.format("Specified data type %s does not support retrieval.", label) + String.format( + Locale.US, + "Specified data type %s does not support retrieval.", + label + ) ) } val classType: ClassType = dataType @@ -2940,6 +2988,7 @@ class Cql2ElmVisitor( propertyException = CqlSemanticException( String.format( + Locale.US, "Could not resolve code path %s for the type of the retrieve %s.", codePath, namedType.name @@ -2998,7 +3047,7 @@ class Cql2ElmVisitor( null ) retrieves.add(mrRetrieve) - mrRetrieve.resultType = ListType(namedType as DataType?) + mrRetrieve.resultType = ListType(namedType as DataType) val mDataType: DataType? = libraryBuilder.resolveTypeName(model, "Medication") val mClassType = mDataType as ClassType val mNamedType: NamedType = mClassType @@ -3016,7 +3065,7 @@ class Cql2ElmVisitor( null ) retrieves.add(mRetrieve) - mRetrieve.resultType = ListType(namedType as DataType?) + mRetrieve.resultType = ListType(namedType as DataType) val q: Query = of.createQuery() val aqs: AliasedQuerySource = of.createAliasedQuerySource().withExpression(mrRetrieve).withAlias("MR") @@ -3057,7 +3106,7 @@ class Cql2ElmVisitor( mCodeComparator = if ( terminology.resultType.isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! ) ) "in" @@ -3097,7 +3146,7 @@ class Cql2ElmVisitor( terminology ) retrieves.add(retrieve) - retrieve.resultType = ListType(namedType as DataType?) + retrieve.resultType = ListType(namedType as DataType) result = if (result == null) { retrieve @@ -3130,7 +3179,7 @@ class Cql2ElmVisitor( terminology ) retrieves.add(retrieve) - retrieve.resultType = ListType(namedType as DataType?) + retrieve.resultType = ListType(namedType as DataType) result = retrieve } return result @@ -3172,12 +3221,12 @@ class Cql2ElmVisitor( if ( (propertyType != null && propertyType.isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! )) ) { if ( terminology.resultType.isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! ) ) "~" @@ -3185,7 +3234,7 @@ class Cql2ElmVisitor( } else { if ( terminology.resultType.isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! ) ) "in" @@ -3223,6 +3272,7 @@ class Cql2ElmVisitor( libraryBuilder.recordParsingException( CqlSemanticException( String.format( + Locale.US, "Unexpected membership operator %s in retrieve", inExpression.javaClass.simpleName ), @@ -3270,7 +3320,7 @@ class Cql2ElmVisitor( equivalent.operand[1] .resultType .isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! )))) ) { retrieve.codes = libraryBuilder.resolveToList(equivalent.operand[1]) @@ -3292,7 +3342,7 @@ class Cql2ElmVisitor( equal.operand[1] .resultType .isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! )))) ) { retrieve.codes = libraryBuilder.resolveToList(equal.operand[1]) @@ -3305,6 +3355,7 @@ class Cql2ElmVisitor( libraryBuilder.recordParsingException( CqlSemanticException( String.format( + Locale.US, "Unknown code comparator %s in retrieve", codeComparator ), @@ -3369,7 +3420,7 @@ class Cql2ElmVisitor( if ( ((libraryBuilder.isCompatibleWith("1.5") && !(terminology.resultType.isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! ))) || (!libraryBuilder.isCompatibleWith("1.5") && terminology.resultType !is ListType)) @@ -3452,10 +3503,8 @@ class Cql2ElmVisitor( } } val qicx: MutableList = ArrayList() - if (ctx.queryInclusionClause() != null) { - for (queryInclusionClauseContext in ctx.queryInclusionClause()) { - qicx.add(visit(queryInclusionClauseContext) as RelationshipClause?) - } + for (queryInclusionClauseContext in ctx.queryInclusionClause()) { + qicx.add(visit(queryInclusionClauseContext) as RelationshipClause?) } var where = if (ctx.whereClause() != null) visit(ctx.whereClause()!!) as Expression? @@ -3655,7 +3704,7 @@ class Cql2ElmVisitor( } else if (property.source != null) { val subPath = getPropertyPath(property.source, alias) if (subPath != null) { - return String.format("%s.%s", subPath, property.path) + return String.format(Locale.US, "%s.%s", subPath, property.path) } } } @@ -3780,9 +3829,11 @@ class Cql2ElmVisitor( * @return `true` if the RHS supports refactoring to a `Retrieve`, `false` otherwise. */ private fun isRHSEligibleForDateRangeOptimization(rhs: Expression): Boolean { - return (rhs.resultType.isSubTypeOf(libraryBuilder.resolveTypeName("System", "DateTime")) || + return (rhs.resultType.isSubTypeOf( + libraryBuilder.resolveTypeName("System", "DateTime")!! + ) || rhs.resultType.isSubTypeOf( - IntervalType(libraryBuilder.resolveTypeName("System", "DateTime")) + IntervalType(libraryBuilder.resolveTypeName("System", "DateTime")!!) )) // BTR: The only requirement for the optimization is that the expression be of @@ -3998,10 +4049,8 @@ class Cql2ElmVisitor( ) } val sortItems: MutableList = ArrayList() - if (ctx.sortByItem() != null) { - for (sortByItemContext in ctx.sortByItem()) { - sortItems.add(visit(sortByItemContext) as SortByItem?) - } + for (sortByItemContext in ctx.sortByItem()) { + sortItems.add(visit(sortByItemContext) as SortByItem?) } return of.createSortClause().withBy(sortItems) } @@ -4101,6 +4150,7 @@ class Cql2ElmVisitor( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to expression %s because its definition contains errors.", expressionInfo.name ) @@ -4208,7 +4258,6 @@ class Cql2ElmVisitor( if (result is FunctionRefInvocation) { if ( result.resolution != null && - result.resolution!!.operator != null && (result.resolution!!.operator.libraryName == null || (result.resolution!!.operator.libraryName == libraryBuilder.compiledLibrary.identifier!!.id)) @@ -4236,9 +4285,6 @@ class Cql2ElmVisitor( // operator compile is working as // expected require(result != null) { "Internal error: could not resolve function" } - require(result.expression != null) { - "Internal error: could not resolve invocation expression" - } require(result.expression.resultType != null) { "Internal error: could not determine result type" } @@ -4260,7 +4306,7 @@ class Cql2ElmVisitor( // NOTE: FHIRPath method invocation // If the target is an expression, resolve as a method invocation - if (target is Expression && isMethodInvocationEnabled) { + if (isMethodInvocationEnabled) { return systemMethodResolver.resolveMethod( target, (identifier)!!, @@ -4271,6 +4317,7 @@ class Cql2ElmVisitor( if (!isMethodInvocationEnabled) { throw CqlCompilerException( String.format( + Locale.US, "The identifier %s could not be resolved as an invocation because method-style invocation is disabled.", identifier ), @@ -4278,7 +4325,7 @@ class Cql2ElmVisitor( ) } throw IllegalArgumentException( - String.format("Invalid invocation target: %s", target.javaClass.name) + String.format(Locale.US, "Invalid invocation target: %s", target.javaClass.name) ) } finally { libraryBuilder.pushExpressionTarget(target) @@ -4377,6 +4424,7 @@ class Cql2ElmVisitor( } else { throw IllegalArgumentException( String.format( + Locale.US, "Internal error attempting to resolve function header for %s", op.name ) @@ -4405,11 +4453,19 @@ class Cql2ElmVisitor( val fd = getFunctionDef(op) ?: throw IllegalArgumentException( - String.format("Could not resolve function header for operator %s", op.name) + String.format( + Locale.US, + "Could not resolve function header for operator %s", + op.name + ) ) return getFunctionHeaderByDef(fd) ?: throw IllegalArgumentException( - String.format("Could not resolve function header for operator %s", op.name) + String.format( + Locale.US, + "Could not resolve function header for operator %s", + op.name + ) ) } @@ -4417,6 +4473,7 @@ class Cql2ElmVisitor( return functionDefinitions[fh] ?: throw IllegalArgumentException( String.format( + Locale.US, "Could not resolve function definition context for function header %s", fh.functionDef.name ) @@ -4438,6 +4495,7 @@ class Cql2ElmVisitor( libraryBuilder.resolveFunctionDefinition(fh.functionDef) ?: throw IllegalArgumentException( String.format( + Locale.US, "Internal error: Could not resolve operator map entry for function header %s", fh.mangledName ) @@ -4474,6 +4532,7 @@ class Cql2ElmVisitor( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Function %s has declared return type %s but the function body returns incompatible type %s.", functionDef.name, resultType.resultType, @@ -4490,6 +4549,7 @@ class Cql2ElmVisitor( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Function %s is marked external but does not declare a return type.", functionDef.name ) @@ -4607,7 +4667,7 @@ class Cql2ElmVisitor( } private fun track(trackable: Trackable?, from: Element): TrackBack? { - val tb = if (from.trackbacks.size > 0) from.trackbacks[0] else null + val tb = if (from.trackbacks.isNotEmpty()) from.trackbacks[0] else null if (tb != null) { trackable!!.trackbacks.add(tb) } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.kt index b065cc1d8..2b117b3b7 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlCompiler.kt @@ -131,8 +131,7 @@ class CqlCompiler( if (offendingSymbol is CommonToken) { builder.recordParsingException( CqlSyntaxException( - @Suppress("ImplicitDefaultLocale") - String.format("Syntax error at %s", offendingSymbol.text), + String.format(Locale.US, "Syntax error at %s", offendingSymbol.text), trackback, e ) diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlTranslatorOptionsMapper.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlTranslatorOptionsMapper.kt index 0ca9653f8..aee5a8da4 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlTranslatorOptionsMapper.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/CqlTranslatorOptionsMapper.kt @@ -4,6 +4,7 @@ package org.cqframework.cql.cql2elm import com.fasterxml.jackson.databind.ObjectMapper import java.io.* +import java.util.* object CqlTranslatorOptionsMapper { private val om: ObjectMapper = ObjectMapper() @@ -15,8 +16,10 @@ object CqlTranslatorOptionsMapper { fr = FileReader(fileName) return fromReader(fr) } catch (@Suppress("SwallowedException") e: IOException) { - @Suppress("TooGenericExceptionThrown", "ImplicitDefaultLocale") - throw RuntimeException(String.format("Errors occurred reading options: %s", e.message)) + @Suppress("TooGenericExceptionThrown") + throw RuntimeException( + String.format(Locale.US, "Errors occurred reading options: %s", e.message) + ) } } @@ -25,8 +28,10 @@ object CqlTranslatorOptionsMapper { try { return om.readValue(reader, CqlTranslatorOptions::class.java) } catch (@Suppress("SwallowedException") e: IOException) { - @Suppress("TooGenericExceptionThrown", "ImplicitDefaultLocale") - throw RuntimeException(String.format("Errors occurred reading options: %s", e.message)) + @Suppress("TooGenericExceptionThrown") + throw RuntimeException( + String.format(Locale.US, "Errors occurred reading options: %s", e.message) + ) } } @@ -37,8 +42,10 @@ object CqlTranslatorOptionsMapper { fw = FileWriter(fileName) toWriter(fw, options) } catch (@Suppress("SwallowedException") e: IOException) { - @Suppress("TooGenericExceptionThrown", "ImplicitDefaultLocale") - throw RuntimeException(String.format("Errors occurred writing options: %s", e.message)) + @Suppress("TooGenericExceptionThrown") + throw RuntimeException( + String.format(Locale.US, "Errors occurred writing options: %s", e.message) + ) } } @@ -48,8 +55,10 @@ object CqlTranslatorOptionsMapper { try { om.writeValue(writer, options) } catch (@Suppress("SwallowedException") e: IOException) { - @Suppress("TooGenericExceptionThrown", "ImplicitDefaultLocale") - throw RuntimeException(String.format("Errors occurred writing options: %s", e.message)) + @Suppress("TooGenericExceptionThrown") + throw RuntimeException( + String.format(Locale.US, "Errors occurred writing options: %s", e.message) + ) } } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DataTypes.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DataTypes.kt index 1d856e826..7d533bd1c 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DataTypes.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DataTypes.kt @@ -1,5 +1,6 @@ package org.cqframework.cql.cql2elm +import java.util.* import org.hl7.cql.model.DataType object DataTypes { @@ -7,8 +8,8 @@ object DataTypes { fun verifyType(actualType: DataType?, expectedType: DataType?) { require(subTypeOf(actualType, expectedType)) { // ERROR: - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Expected an expression of type '%s', but found an expression of type '%s'.", if (expectedType != null) expectedType.toLabel() else "", if (actualType != null) actualType.toLabel() else "" @@ -25,8 +26,8 @@ object DataTypes { compatibleWith(sourceType, targetType) ) { // ERROR: - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Expression of type '%s' cannot be cast as a value of type '%s'.", if (sourceType != null) sourceType.toLabel() else "", if (targetType != null) targetType.toLabel() else "" diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceLoader.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceLoader.kt index da136a977..2d50a776a 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceLoader.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceLoader.kt @@ -2,6 +2,8 @@ package org.cqframework.cql.cql2elm import java.io.InputStream import java.nio.file.Path +import java.util.* +import kotlin.collections.ArrayList import org.hl7.cql.model.NamespaceAware import org.hl7.cql.model.NamespaceManager import org.hl7.elm.r1.VersionedIdentifier @@ -15,30 +17,27 @@ internal class DefaultLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, private val providers: MutableList = ArrayList() var initialized: Boolean = false - override fun registerProvider(provider: LibrarySourceProvider?) { - require(provider != null) { "provider is null." } - if (provider is NamespaceAware) { - (provider as NamespaceAware).setNamespaceManager(namespaceManager) + override fun registerProvider(provider: LibrarySourceProvider) { + if (namespaceManager != null && provider is NamespaceAware) { + provider.setNamespaceManager(namespaceManager!!) } - if (provider is PathAware) { - (provider as PathAware).setPath(path) + if (path != null && provider is PathAware) { + provider.setPath(path!!) } providers.add(provider) } private var path: Path? = null - override fun setPath(path: Path?) { - if (path == null || !path.toFile().isDirectory) { - throw IllegalArgumentException( - @Suppress("ImplicitDefaultLocale") - String.format("path '%s' is not a valid directory", path) - ) + override fun setPath(path: Path) { + require(path.toFile().isDirectory) { + String.format(Locale.US, "path '%s' is not a valid directory", path) } + this.path = path - for (provider: LibrarySourceProvider? in getProviders()) { + for (provider in getProviders()) { if (provider is PathAware) { - (provider as PathAware).setPath(path) + provider.setPath(path) } } } @@ -60,16 +59,14 @@ internal class DefaultLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, return providers } - override fun getLibrarySource(libraryIdentifier: VersionedIdentifier?): InputStream { - require(libraryIdentifier != null) { "libraryIdentifier is null." } - require(!libraryIdentifier.id.isNullOrEmpty()) { "libraryIdentifier Id is null." } + override fun getLibrarySource(libraryIdentifier: VersionedIdentifier): InputStream { var source: InputStream? = null for (provider: LibrarySourceProvider in getProviders()) { val localSource: InputStream? = provider.getLibrarySource(libraryIdentifier) if (localSource != null) { require(source == null) { - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Multiple sources found for library %s, version %s.", libraryIdentifier.id, libraryIdentifier.version @@ -80,8 +77,8 @@ internal class DefaultLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, } if (source == null) { throw IllegalArgumentException( - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Could not load source for library %s, version %s.", libraryIdentifier.id, libraryIdentifier.version diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceProvider.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceProvider.kt index 6a73d128b..6ed22f74d 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceProvider.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultLibrarySourceProvider.kt @@ -4,6 +4,7 @@ package org.cqframework.cql.cql2elm import java.io.* import java.nio.file.Path +import java.util.* import org.cqframework.cql.cql2elm.model.Version import org.hl7.elm.r1.VersionedIdentifier @@ -13,19 +14,16 @@ import org.hl7.elm.r1.VersionedIdentifier // form // [.[.]] // Usage outside these boundaries will result in errors or incorrect behavior. -@Suppress("ImplicitDefaultLocale") -class DefaultLibrarySourceProvider(path: Path?) : LibrarySourceProvider, PathAware { +class DefaultLibrarySourceProvider(path: Path) : LibrarySourceProvider, PathAware { private var path: Path? = null init { - setPath(path) + this.setPath(path) } - override fun setPath(path: Path?) { - if (path == null || !path.toFile().isDirectory) { - throw IllegalArgumentException( - String.format("path '%s' is not a valid directory", path) - ) + override fun setPath(path: Path) { + require(path.toFile().isDirectory) { + String.format(Locale.US, "path '%s' is not a valid directory", path) } this.path = path } @@ -38,6 +36,7 @@ class DefaultLibrarySourceProvider(path: Path?) : LibrarySourceProvider, PathAwa val libraryPath: Path = currentPath.resolve( String.format( + Locale.US, "%s%s.cql", libraryName, if (libraryIdentifier.version != null) ("-" + libraryIdentifier.version) @@ -107,7 +106,11 @@ class DefaultLibrarySourceProvider(path: Path?) : LibrarySourceProvider, PathAwa } } catch (e: FileNotFoundException) { throw IllegalArgumentException( - String.format("Could not load source for library %s.", libraryIdentifier.id), + String.format( + Locale.US, + "Could not load source for library %s.", + libraryIdentifier.id + ), e ) } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultModelInfoProvider.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultModelInfoProvider.kt index 7ab382e9e..e998794b1 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultModelInfoProvider.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/DefaultModelInfoProvider.kt @@ -17,19 +17,18 @@ import org.hl7.elm_modelinfo.r1.serializing.ModelInfoReaderFactory // form // [.[.]] // Usage outside these boundaries will result in errors or incorrect behavior. -@Suppress("ImplicitDefaultLocale") class DefaultModelInfoProvider : ModelInfoProvider, PathAware { constructor() - constructor(path: Path?) { - setPath(path) + constructor(path: Path) { + this.setPath(path) } private var path: Path? = null - override fun setPath(path: Path?) { - require(!(path == null || !path.toFile().isDirectory)) { - String.format("path '%s' is not a valid directory", path) + override fun setPath(path: Path) { + require(path.toFile().isDirectory) { + String.format(Locale.US, "path '%s' is not a valid directory", path) } this.path = path } @@ -50,6 +49,7 @@ class DefaultModelInfoProvider : ModelInfoProvider, PathAware { val modelPath = currentPath.resolve( String.format( + Locale.US, "%s-modelinfo%s.xml", modelName.lowercase(Locale.getDefault()), if (modelVersion != null) "-$modelVersion" else "" @@ -111,10 +111,11 @@ class DefaultModelInfoProvider : ModelInfoProvider, PathAware { } try { val inputStream: InputStream = FileInputStream(modelFile) - return ModelInfoReaderFactory.getReader("application/xml").read(inputStream) + return ModelInfoReaderFactory.getReader("application/xml")?.read(inputStream) } catch (e: IOException) { throw IllegalArgumentException( String.format( + Locale.US, "Could not load definition for model info %s.", modelIdentifier.id ), diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.kt index 31c8ca29f..288187bf9 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.kt @@ -17,14 +17,7 @@ import org.hl7.cql_annotations.r1.ObjectFactory import org.hl7.elm.r1.* /** Created by Bryn on 12/29/2016. */ -@Suppress( - "LargeClass", - "TooManyFunctions", - "ImplicitDefaultLocale", - "ForbiddenComment", - "ReturnCount", - "MaxLineLength" -) +@Suppress("LargeClass", "TooManyFunctions", "ForbiddenComment", "ReturnCount", "MaxLineLength") class LibraryBuilder( @JvmField val namespaceInfo: NamespaceInfo?, // Note: allowed to be null, implies global namespace @@ -177,6 +170,7 @@ class LibraryBuilder( } require(isCompatibleWith(sinceCompatibilityLevel)) { String.format( + Locale.US, "Feature %s was introduced in version %s and so cannot be used at compatibility level %s", featureName, sinceCompatibilityLevel, @@ -223,6 +217,7 @@ class LibraryBuilder( ) { throw IllegalArgumentException( String.format( + Locale.US, "Could not load model information for model %s, version %s because version %s is already loaded.", modelIdentifier.id, modelIdentifier.version, @@ -297,6 +292,7 @@ class LibraryBuilder( if (result != null) { throw IllegalArgumentException( String.format( + Locale.US, "Label %s is ambiguous between %s and %s.", label, result.label, @@ -333,6 +329,7 @@ class LibraryBuilder( if (result != null) { throw IllegalArgumentException( String.format( + Locale.US, "Context name %s is ambiguous between %s and %s.", contextName, result.name, @@ -374,6 +371,7 @@ class LibraryBuilder( if (result != null) { throw IllegalArgumentException( String.format( + Locale.US, "Type name %s is ambiguous between %s and %s.", typeName, (result as NamedType).name, @@ -405,6 +403,7 @@ class LibraryBuilder( if (!isCompatibleWith("1.5") && !isFHIRHelpers(compiledLibrary)) { throw IllegalArgumentException( String.format( + Locale.US, "The type %s was introduced in CQL 1.5 and cannot be referenced at compatibility level %s", (result as NamedType).name, compatibilityLevel @@ -430,30 +429,34 @@ class LibraryBuilder( // simpleTypeSpecifier: (identifier '.')? identifier // intervalTypeSpecifier: 'interval' '<' typeSpecifier '>' // listTypeSpecifier: 'list' '<' typeSpecifier '>' - return if (typeSpecifier.lowercase(Locale.getDefault()).startsWith("interval<")) { - val pointType = - resolveTypeSpecifier( - typeSpecifier.substring( - typeSpecifier.indexOf('<') + 1, - typeSpecifier.lastIndexOf('>') - ) - ) - IntervalType(pointType) - } else if (typeSpecifier.lowercase(Locale.getDefault()).startsWith("list<")) { - val elementType = - resolveTypeName( - typeSpecifier.substring( - typeSpecifier.indexOf('<') + 1, - typeSpecifier.lastIndexOf('>') + when { + typeSpecifier.lowercase(Locale.getDefault()).startsWith("interval<") -> { + val pointType = + resolveTypeSpecifier( + typeSpecifier.substring( + typeSpecifier.indexOf('<') + 1, + typeSpecifier.lastIndexOf('>') + ) ) - ) - ListType(elementType) - } else if (typeSpecifier.indexOf(".") >= 0) { - val modelName = typeSpecifier.substring(0, typeSpecifier.indexOf(".")) - val typeName = typeSpecifier.substring(typeSpecifier.indexOf(".") + 1) - resolveTypeName(modelName, typeName) - } else { - resolveTypeName(typeSpecifier) + return IntervalType(pointType!!) + } + else -> + return if (typeSpecifier.lowercase(Locale.getDefault()).startsWith("list<")) { + val elementType = + resolveTypeName( + typeSpecifier.substring( + typeSpecifier.indexOf('<') + 1, + typeSpecifier.lastIndexOf('>') + ) + ) + ListType(elementType!!) + } else if (typeSpecifier.indexOf(".") >= 0) { + val modelName = typeSpecifier.substring(0, typeSpecifier.indexOf(".")) + val typeName = typeSpecifier.substring(typeSpecifier.indexOf(".") + 1) + resolveTypeName(modelName, typeName) + } else { + resolveTypeName(typeSpecifier) + } } } @@ -463,27 +466,27 @@ class LibraryBuilder( val systemModel: SystemModel get() = // TODO: Support loading different versions of the system library - getModel(ModelIdentifier().withId("System"), "System") as SystemModel + getModel(ModelIdentifier("System"), "System") as SystemModel fun getModel(modelName: String): Model { val usingDef = resolveUsingRef(modelName) if (usingDef == null && modelName == "FHIR") { // Special case for FHIR-derived models that include FHIR Helpers - val model = modelManager.resolveModelByUri("http://hl7.org/fhir") - if (model != null) { - return model - } + return modelManager.resolveModelByUri("http://hl7.org/fhir") + } + requireNotNull(usingDef) { + String.format(Locale.US, "Could not resolve model name %s", modelName) } - requireNotNull(usingDef) { String.format("Could not resolve model name %s", modelName) } return getModel(usingDef) } fun getModel(usingDef: UsingDef): Model { return getModel( - ModelIdentifier() - .withSystem(NamespaceManager.getUriPart(usingDef.uri)) - .withId(NamespaceManager.getNamePart(usingDef.uri)) - .withVersion(usingDef.version), + ModelIdentifier( + id = NamespaceManager.getNamePart(usingDef.uri)!!, + system = NamespaceManager.getUriPart(usingDef.uri), + version = usingDef.version + ), usingDef.localIdentifier ) } @@ -509,14 +512,14 @@ class LibraryBuilder( } return libraries[identifier] ?: throw IllegalArgumentException( - String.format("Could not resolve library name %s.", identifier) + String.format(Locale.US, "Could not resolve library name %s.", identifier) ) } - fun resolveNamespaceUri(namespaceName: String?, mustResolve: Boolean): String? { + fun resolveNamespaceUri(namespaceName: String, mustResolve: Boolean): String? { val namespaceUri = libraryManager.namespaceManager.resolveNamespaceUri(namespaceName) require(!(namespaceUri == null && mustResolve)) { - String.format("Could not resolve namespace name %s", namespaceName) + String.format(Locale.US, "Could not resolve namespace name %s", namespaceName) } return namespaceUri } @@ -564,7 +567,7 @@ class LibraryBuilder( errorSeverity == CqlCompilerException.ErrorSeverity.Error else -> throw IllegalArgumentException( - String.format("Unknown error severity %s", errorSeverity.toString()) + String.format(Locale.US, "Unknown error severity %s", errorSeverity.toString()) ) } } @@ -1019,7 +1022,7 @@ class LibraryBuilder( if ( right is ValueSetRef || (isCompatibleWith("1.5") && - right.resultType.isCompatibleWith(resolveTypeName("System", "ValueSet")) && + right.resultType.isCompatibleWith(resolveTypeName("System", "ValueSet")!!) && right.resultType != resolveTypeName("System", "Any")) ) { if (left.resultType is ListType) { @@ -1045,7 +1048,7 @@ class LibraryBuilder( if ( right is CodeSystemRef || (isCompatibleWith("1.5") && - right.resultType.isCompatibleWith(resolveTypeName("System", "CodeSystem")) && + right.resultType.isCompatibleWith(resolveTypeName("System", "CodeSystem")!!) && right.resultType != resolveTypeName("System", "Any")) ) { if (left.resultType is ListType) { @@ -1368,6 +1371,7 @@ class LibraryBuilder( for (operand in operands) { require(!(operand == null || operand.resultType == null)) { String.format( + Locale.US, "Could not determine signature for invocation of operator %s%s.", if (libraryName == null) "" else "$libraryName.", operatorName @@ -1481,6 +1485,7 @@ class LibraryBuilder( for (operand in fd.operand) { require(!(operand == null || operand.resultType == null)) { String.format( + Locale.US, "Could not determine signature for invocation of operator %s%s.", if (libraryName == null) "" else "$libraryName.", operatorName @@ -1560,6 +1565,7 @@ class LibraryBuilder( requireNotNull(resolution) { // ERROR: String.format( + Locale.US, "Could not resolve call to operator %s with signature %s.", callContext.operatorName, callContext.signature @@ -1568,6 +1574,7 @@ class LibraryBuilder( if (resolution.operator.fluent && !callContext.allowFluent) { throw IllegalArgumentException( String.format( + Locale.US, "Operator %s with signature %s is a fluent function and can only be invoked with fluent syntax.", callContext.operatorName, callContext.signature @@ -1577,6 +1584,7 @@ class LibraryBuilder( if (callContext.allowFluent && !resolution.operator.fluent && !resolution.allowFluent) { throw IllegalArgumentException( String.format( + Locale.US, "Invocation of operator %s with signature %s uses fluent syntax, but the operator is not defined as a fluent function.", callContext.operatorName, callContext.signature @@ -1597,6 +1605,7 @@ class LibraryBuilder( // ERROR: throw CqlSemanticException( String.format( + Locale.US, "Identifier %s in library %s is marked private and cannot be referenced from another library.", objectName, libraryName @@ -2238,8 +2247,8 @@ class LibraryBuilder( if (compatibleType != null) { return compatibleType } - if (!second.isSubTypeOf(first)) { - return ChoiceType(listOf(first, second)) + if (first != null && !second.isSubTypeOf(first)) { + return ChoiceType(first, second) } // The above construction of a choice type guarantees this will never be hit @@ -2402,7 +2411,7 @@ class LibraryBuilder( .withHighClosed(highClosed) val pointType: DataType? = ensureCompatibleTypes(result.low.resultType, result.high.resultType) - result.resultType = IntervalType(pointType) + result.resultType = IntervalType(pointType!!) result.low = ensureCompatible(result.low, pointType) result.high = ensureCompatible(result.high, pointType) return result @@ -2454,7 +2463,7 @@ class LibraryBuilder( val classType: ClassType = currentType if (identifier.startsWith("?") && isCompatibleWith("1.5")) { val searchPath: String = identifier.substring(1) - for (s: SearchType in classType.searches) { + for (s: SearchType in classType.getSearches()) { if ((s.name == searchPath)) { return PropertyResolution(s) } @@ -2462,9 +2471,10 @@ class LibraryBuilder( } else { for (e: ClassTypeElement in classType.elements) { if ((e.name == identifier)) { - if (e.isProhibited) { + if (e.prohibited) { throw IllegalArgumentException( String.format( + Locale.US, "Element %s cannot be referenced because it is marked prohibited in type %s.", e.name, currentType.name @@ -2490,7 +2500,11 @@ class LibraryBuilder( PropertyResolution((resolveTypeName("System", "Boolean"))!!, identifier) else -> // ERROR: throw IllegalArgumentException( - String.format("Invalid interval property name %s.", identifier) + String.format( + Locale.US, + "Invalid interval property name %s.", + identifier + ) ) } } else if (currentType is ChoiceType) { @@ -2510,6 +2524,7 @@ class LibraryBuilder( if (resultTargetMaps[resolution.type] != resolution.targetMap) { throw IllegalArgumentException( String.format( + Locale.US, "Inconsistent target maps %s and %s for choice type %s", resultTargetMaps[resolution.type], resolution.targetMap, @@ -2526,6 +2541,7 @@ class LibraryBuilder( } else if (name != resolution.name) { throw IllegalArgumentException( String.format( + Locale.US, "Inconsistent property resolution for choice type %s (was %s, is %s)", choice.toString(), name, @@ -2563,7 +2579,12 @@ class LibraryBuilder( if (mustResolve) { // ERROR: throw IllegalArgumentException( - String.format("Member %s not found for type %s.", identifier, sourceType?.toLabel()) + String.format( + Locale.US, + "Member %s not found for type %s.", + identifier, + sourceType?.toLabel() + ) ) } return null @@ -2650,6 +2671,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to expression %s because its definition contains errors.", expressionRef.name ) @@ -2666,6 +2688,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to parameter %s because its definition contains errors.", parameterRef.name ) @@ -2682,6 +2705,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to valueset %s because its definition contains errors.", valuesetRef.name ) @@ -2701,6 +2725,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to codesystem %s because its definition contains errors.", codesystemRef.name ) @@ -2716,6 +2741,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to code %s because its definition contains errors.", codeRef.name ) @@ -2731,6 +2757,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to concept %s because its definition contains error.", conceptRef.name ) @@ -2770,6 +2797,7 @@ class LibraryBuilder( if (message == null) { message = String.format( + Locale.US, "Could not resolve identifier %s in the current library.", identifier ) @@ -2806,6 +2834,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not validate reference to parameter %s because its definition contains errors.", parameterRef.name ) @@ -2861,6 +2890,7 @@ class LibraryBuilder( if (result != null) { reportWarning( String.format( + Locale.US, "Duplicate mapped model %s:%s%s", model.modelInfo.name, model.modelInfo.targetUrl, @@ -2955,7 +2985,11 @@ class LibraryBuilder( val splitIndex: Int = typeCase.indexOf(':') if (splitIndex <= 0) { throw IllegalArgumentException( - String.format("Malformed type case in targetMap %s", targetMap) + String.format( + Locale.US, + "Malformed type case in targetMap %s", + targetMap + ) ) } val typeCaseElement: String = typeCase.substring(0, splitIndex) @@ -3008,43 +3042,48 @@ class LibraryBuilder( val argumentSource: Expression? = if ((functionArgument == "%value")) source else applyTargetMap(source, functionArgument) - if (argumentSource!!.resultType is ListType) { - val query: Query = - objectFactory - .createQuery() - .withSource( - objectFactory - .createAliasedQuerySource() - .withExpression(argumentSource) - .withAlias("\$this") - ) - val fr: FunctionRef = - objectFactory - .createFunctionRef() - .withLibraryName(libraryName) - .withName(functionName) - .withOperand(objectFactory.createAliasRef().withName("\$this")) - // This doesn't quite work because the US.Core types aren't subtypes of FHIR types. - // resolveCall(libraryName, functionName, new FunctionRefInvocation(fr), false, - // false); - query.setReturn( - objectFactory.createReturnClause().withDistinct(false).withExpression(fr) - ) - query.resultType = source!!.resultType - return query - } else { - val fr: FunctionRef = - objectFactory - .createFunctionRef() - .withLibraryName(libraryName) - .withName(functionName) - .withOperand(argumentSource) - fr.resultType = source!!.resultType - return fr - // This doesn't quite work because the US.Core types aren't subtypes of FHIR types, - // or they are defined as System types and not FHIR types - // return resolveCall(libraryName, functionName, new FunctionRefInvocation(fr), - // false, false); + when (argumentSource!!.resultType) { + is ListType -> { + val query: Query = + objectFactory + .createQuery() + .withSource( + objectFactory + .createAliasedQuerySource() + .withExpression(argumentSource) + .withAlias("\$this") + ) + val fr: FunctionRef = + objectFactory + .createFunctionRef() + .withLibraryName(libraryName) + .withName(functionName) + .withOperand(objectFactory.createAliasRef().withName("\$this")) + // This doesn't quite work because the US.Core types aren't subtypes of FHIR + // types. + // resolveCall(libraryName, functionName, new FunctionRefInvocation(fr), false, + // false); + query.setReturn( + objectFactory.createReturnClause().withDistinct(false).withExpression(fr) + ) + query.resultType = source!!.resultType + return query + } + else -> { + val fr: FunctionRef = + objectFactory + .createFunctionRef() + .withLibraryName(libraryName) + .withName(functionName) + .withOperand(argumentSource) + fr.resultType = source!!.resultType + return fr + // This doesn't quite work because the US.Core types aren't subtypes of FHIR + // types, + // or they are defined as System types and not FHIR types + // return resolveCall(libraryName, functionName, new FunctionRefInvocation(fr), + // false, false); + } } } else if (targetMap.contains("[")) { val indexerStart: Int = targetMap.indexOf("[") @@ -3061,6 +3100,7 @@ class LibraryBuilder( if (source !is Property) { throw IllegalArgumentException( String.format( + Locale.US, "Cannot expand target map %s for non-property-accessor type %s", targetMap, source!!.javaClass.simpleName @@ -3076,6 +3116,7 @@ class LibraryBuilder( } else { throw IllegalArgumentException( String.format( + Locale.US, "Cannot resolve %%parent reference in targetMap %s", targetMap ) @@ -3100,6 +3141,7 @@ class LibraryBuilder( if (indexerItems.size != 2) { throw IllegalArgumentException( String.format( + Locale.US, "Invalid indexer item %s in targetMap %s", indexerItem, targetMap @@ -3262,7 +3304,7 @@ class LibraryBuilder( } } throw IllegalArgumentException( - String.format("TargetMapping not implemented: %s", targetMap) + String.format(Locale.US, "TargetMapping not implemented: %s", targetMap) ) } @@ -3360,6 +3402,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not resolve identifier %s in library %s.", memberIdentifier, referencedLibrary.identifier!!.id @@ -3545,6 +3588,7 @@ class LibraryBuilder( } throw IllegalArgumentException( String.format( + Locale.US, "Invalid context reference from %s context to %s context.", currentExpressionContext(), expressionDef.context @@ -3677,12 +3721,14 @@ class LibraryBuilder( val elementString = lookupElementWarning(trackable) return if (trackable is Literal) { String.format( + Locale.US, "You used a string literal: [%s] here that matches an identifier in scope: [%s]. Did you mean to use the identifier instead?", identifierParam, matchedIdentifier ) } else String.format( + Locale.US, "%s identifier [%s] is hiding another identifier of the same name.", elementString, identifierParam @@ -3732,6 +3778,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Cannot resolve reference to expression or function %s because it results in a circular reference.", identifier ) diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryManager.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryManager.kt index 7c3886c99..d475d2c65 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryManager.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryManager.kt @@ -3,6 +3,10 @@ package org.cqframework.cql.cql2elm import java.io.* +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.HashMap +import kotlin.collections.HashSet import org.apache.commons.lang3.StringUtils import org.cqframework.cql.cql2elm.model.CompiledLibrary import org.cqframework.cql.elm.serializing.ElmLibraryReaderFactory @@ -18,7 +22,7 @@ import org.slf4j.LoggerFactory * Manages a set of CQL libraries. As new library references are encountered during compilation, the * corresponding source is obtained via librarySourceLoader, compiled and cached for later use. */ -@Suppress("TooManyFunctions", "ImplicitDefaultLocale") +@Suppress("TooManyFunctions") class LibraryManager @JvmOverloads constructor( @@ -32,7 +36,7 @@ constructor( READ_WRITE } - var namespaceManager = modelManager.getNamespaceManager() + var namespaceManager = modelManager.namespaceManager var compiledLibraries: MutableMap = libraryCache ?: HashMap() val librarySourceLoader: LibrarySourceLoader = PriorityLibrarySourceLoader() @@ -125,6 +129,7 @@ constructor( librarySourceLoader.getLibrarySource(libraryIdentifier) ?: throw CqlIncludeException( String.format( + Locale.US, "Could not load source for library %s, version %s.", libraryIdentifier.id, libraryIdentifier.version @@ -135,7 +140,7 @@ constructor( ) val compiler = CqlCompiler( - namespaceManager.getNamespaceInfoFromUri(libraryIdentifier.system), + libraryIdentifier.system?.let { namespaceManager.getNamespaceInfoFromUri(it) }, libraryIdentifier, this ) @@ -148,6 +153,7 @@ constructor( ) { throw CqlIncludeException( String.format( + Locale.US, "Library %s was included as version %s, but version %s of the library was found.", libraryPath, libraryIdentifier.version, @@ -161,6 +167,7 @@ constructor( } catch (e: IOException) { throw CqlIncludeException( String.format( + Locale.US, "Errors occurred translating library %s, version %s.", libraryPath, libraryIdentifier.version @@ -174,6 +181,7 @@ constructor( if (result == null) { throw CqlIncludeException( String.format( + Locale.US, "Could not load source for library %s, version %s.", libraryPath, libraryIdentifier.version diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryReaderUtil.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryReaderUtil.kt index 78d8e5a74..46ebd90be 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryReaderUtil.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryReaderUtil.kt @@ -6,6 +6,7 @@ import java.io.* import java.net.URI import java.net.URISyntaxException import java.net.URL +import java.util.* import javax.xml.transform.Source import javax.xml.transform.stream.StreamSource @@ -42,8 +43,11 @@ object LibraryReaderUtil { return json } throw CqlCompilerException( - @Suppress("ImplicitDefaultLocale") - String.format("Could not determine access path for input of type %s.", json!!.javaClass) + String.format( + Locale.US, + "Could not determine access path for input of type %s.", + json!!.javaClass + ) ) } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibrarySourceLoader.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibrarySourceLoader.kt index f7b4f03ff..213b2a92a 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibrarySourceLoader.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibrarySourceLoader.kt @@ -7,12 +7,12 @@ import org.hl7.elm.r1.VersionedIdentifier interface LibrarySourceLoader { fun clearProviders() - fun getLibrarySource(libraryIdentifier: VersionedIdentifier?): InputStream? + fun getLibrarySource(libraryIdentifier: VersionedIdentifier): InputStream? - fun registerProvider(provider: LibrarySourceProvider?) + fun registerProvider(provider: LibrarySourceProvider) fun getLibraryContent( - libraryIdentifier: VersionedIdentifier?, + libraryIdentifier: VersionedIdentifier, type: LibraryContentType ): InputStream? { if (LibraryContentType.CQL == type) { @@ -22,7 +22,7 @@ interface LibrarySourceLoader { } fun isLibraryContentAvailable( - libraryIdentifier: VersionedIdentifier?, + libraryIdentifier: VersionedIdentifier, type: LibraryContentType ): Boolean { if (LibraryContentType.CQL == type) { diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelInfoLoader.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelInfoLoader.kt index 7546f0823..63f37a096 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelInfoLoader.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelInfoLoader.kt @@ -1,6 +1,8 @@ package org.cqframework.cql.cql2elm import java.nio.file.Path +import java.util.* +import kotlin.collections.ArrayList import org.hl7.cql.model.ModelIdentifier import org.hl7.cql.model.ModelInfoProvider import org.hl7.cql.model.NamespaceAware @@ -26,7 +28,6 @@ class ModelInfoLoader : NamespaceAware, PathAware { } fun getModelInfo(modelIdentifier: ModelIdentifier): ModelInfo { - checkModelIdentifier(modelIdentifier) var modelInfo: ModelInfo? = null for (provider in getProviders()) { modelInfo = provider.load(modelIdentifier) @@ -35,8 +36,8 @@ class ModelInfoLoader : NamespaceAware, PathAware { } } requireNotNull(modelInfo) { - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Could not resolve model info provider for model %s, version %s.", if (modelIdentifier.system == null) modelIdentifier.id else NamespaceManager.getPath(modelIdentifier.system, modelIdentifier.id), @@ -47,18 +48,14 @@ class ModelInfoLoader : NamespaceAware, PathAware { } @JvmOverloads - fun registerModelInfoProvider(provider: ModelInfoProvider?, priority: Boolean = false) { - requireNotNull(provider) { "Provider is null" } - if (namespaceManager != null) { - if (provider is NamespaceAware) { - (provider as NamespaceAware).setNamespaceManager(namespaceManager) - } + fun registerModelInfoProvider(provider: ModelInfoProvider, priority: Boolean = false) { + if (namespaceManager != null && provider is NamespaceAware) { + provider.setNamespaceManager(namespaceManager!!) } - if (path != null) { - if (provider is PathAware) { - (provider as PathAware).setPath(path) - } + if (path != null && provider is PathAware) { + provider.setPath(path!!) } + if (priority) { providers.add(0, provider) } else { @@ -75,13 +72,6 @@ class ModelInfoLoader : NamespaceAware, PathAware { initialized = false } - private fun checkModelIdentifier(modelIdentifier: ModelIdentifier?) { - requireNotNull(modelIdentifier) { "modelIdentifier is null." } - require(!(modelIdentifier.id == null || modelIdentifier.id == "")) { - "modelIdentifier Id is null or empty." - } - } - override fun setNamespaceManager(namespaceManager: NamespaceManager) { this.namespaceManager = namespaceManager for (provider in getProviders()) { @@ -91,15 +81,14 @@ class ModelInfoLoader : NamespaceAware, PathAware { } } - override fun setPath(path: Path?) { - require(!(path == null || !path.toFile().isDirectory)) { - @Suppress("ImplicitDefaultLocale") - String.format("path '%s' is not a valid directory", path) + override fun setPath(path: Path) { + require(path.toFile().isDirectory) { + String.format(Locale.US, "path '%s' is not a valid directory", path) } this.path = path for (provider in getProviders()) { if (provider is PathAware) { - (provider as PathAware).setPath(path) + provider.setPath(path) } } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelManager.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelManager.kt index 22b405366..c5c14f28c 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelManager.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelManager.kt @@ -11,7 +11,7 @@ import org.hl7.cql.model.* /** Created by Bryn on 12/29/2016. */ @Suppress("TooManyFunctions") class ModelManager { - private val namespaceManager: NamespaceManager + val namespaceManager: NamespaceManager private var path: Path? = null var modelInfoLoader: ModelInfoLoader? = null private set @@ -31,10 +31,6 @@ class ModelManager { /** @param globalCache cache for Models by ModelIdentifier. Expected to be thread-safe. */ constructor(globalCache: MutableMap) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) namespaceManager = NamespaceManager() this.globalCache = globalCache initialize() @@ -48,10 +44,6 @@ class ModelManager { } constructor(path: Path?, globalCache: MutableMap) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) namespaceManager = NamespaceManager() this.globalCache = globalCache this.path = path @@ -69,10 +61,6 @@ class ModelManager { enableDefaultModelInfoLoading: Boolean, globalCache: MutableMap ) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) namespaceManager = NamespaceManager() this.globalCache = globalCache isDefaultModelInfoLoadingEnabled = enableDefaultModelInfoLoading @@ -92,10 +80,6 @@ class ModelManager { path: Path?, globalCache: MutableMap ) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) namespaceManager = NamespaceManager() this.globalCache = globalCache this.path = path @@ -113,10 +97,6 @@ class ModelManager { namespaceManager: NamespaceManager, globalCache: MutableMap ) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) this.namespaceManager = namespaceManager this.globalCache = globalCache initialize() @@ -134,10 +114,6 @@ class ModelManager { path: Path?, globalCache: MutableMap ) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) this.namespaceManager = namespaceManager this.globalCache = globalCache this.path = path @@ -156,10 +132,6 @@ class ModelManager { enableDefaultModelInfoLoading: Boolean, globalCache: MutableMap ) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) this.namespaceManager = namespaceManager this.globalCache = globalCache isDefaultModelInfoLoadingEnabled = enableDefaultModelInfoLoading @@ -184,10 +156,6 @@ class ModelManager { path: Path?, globalCache: MutableMap ) { - Objects.requireNonNull>( - globalCache, - "globalCache can not be null." - ) this.namespaceManager = namespaceManager this.globalCache = globalCache this.path = path @@ -199,14 +167,10 @@ class ModelManager { modelInfoLoader = ModelInfoLoader() modelInfoLoader!!.setNamespaceManager(namespaceManager) if (path != null) { - modelInfoLoader!!.setPath(path) + modelInfoLoader!!.setPath(path!!) } } - fun getNamespaceManager(): NamespaceManager { - return namespaceManager - } - /** * The global cache is by @{org.hl7.cql.model.ModelIdentifier}, while the local cache is by * name. This is because the translator expects the ModelManager to only permit loading of a @@ -234,12 +198,9 @@ class ModelManager { } } - private fun buildModel(identifier: ModelIdentifier?): Model? { + private fun buildModel(identifier: ModelIdentifier): Model? { val model: Model? - requireNotNull(identifier) { "Model identifier is required" } - require(!(identifier.id == null || identifier.id == "")) { - "Model identifier Id is required" - } + require(identifier.id.isNotEmpty()) { "Model identifier Id is required" } val modelPath = NamespaceManager.getPath(identifier.system, identifier.id) pushLoading(modelPath) model = @@ -258,7 +219,7 @@ class ModelManager { private fun pushLoading(modelId: String) { require(!loadingModels.contains(modelId)) { - @Suppress("ImplicitDefaultLocale") String.format("Circular model reference %s", modelId) + String.format(Locale.US, "Circular model reference %s", modelId) } loadingModels.add(modelId) } @@ -269,7 +230,7 @@ class ModelManager { @JvmOverloads fun resolveModel(modelName: String, version: String? = null): Model { - return resolveModel(ModelIdentifier().withId(modelName).withVersion(version)) + return resolveModel(ModelIdentifier(modelName, version = version)) } /** @@ -301,8 +262,8 @@ class ModelManager { !(modelIdentifier.version != null && modelIdentifier.version != model!!.modelInfo.version) ) { - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Could not load model information for model %s, version %s because version %s is already loaded.", modelIdentifier.id, modelIdentifier.version, @@ -314,8 +275,7 @@ class ModelManager { fun resolveModelByUri(namespaceUri: String): Model { return modelsByUri[namespaceUri] ?: throw IllegalArgumentException( - @Suppress("ImplicitDefaultLocale") - String.format("Could not resolve model with namespace %s", namespaceUri) + String.format(Locale.US, "Could not resolve model with namespace %s", namespaceUri) ) } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PathAware.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PathAware.kt index bd4a70bb1..94cb02962 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PathAware.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PathAware.kt @@ -3,5 +3,5 @@ package org.cqframework.cql.cql2elm import java.nio.file.Path interface PathAware { - fun setPath(path: Path?) + fun setPath(path: Path) } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PriorityLibrarySourceLoader.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PriorityLibrarySourceLoader.kt index fbabf7480..38ae54e6f 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PriorityLibrarySourceLoader.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/PriorityLibrarySourceLoader.kt @@ -2,41 +2,42 @@ package org.cqframework.cql.cql2elm import java.io.InputStream import java.nio.file.Path +import java.util.* +import kotlin.collections.ArrayList import org.hl7.cql.model.NamespaceAware import org.hl7.cql.model.NamespaceManager import org.hl7.elm.r1.VersionedIdentifier /** * Used by LibraryManager to manage a set of library source providers that resolve library includes - * within CQL. Package private since its not intended to be used outside the context of the + * within CQL. Package private since it's not intended to be used outside the context of the * instantiating LibraryManager instance. */ class PriorityLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, PathAware { private val providers: MutableList = ArrayList() private var initialized = false - override fun registerProvider(provider: LibrarySourceProvider?) { - requireNotNull(provider) { "provider is null." } - if (provider is NamespaceAware) { - (provider as NamespaceAware).setNamespaceManager(namespaceManager) + override fun registerProvider(provider: LibrarySourceProvider) { + if (namespaceManager != null && provider is NamespaceAware) { + provider.setNamespaceManager(namespaceManager!!) } + if (path != null && provider is PathAware) { - (provider as PathAware).setPath(path) + provider.setPath(path!!) } providers.add(provider) } private var path: Path? = null - override fun setPath(path: Path?) { - require(!(path == null || !path.toFile().isDirectory)) { - @Suppress("ImplicitDefaultLocale") - String.format("path '%s' is not a valid directory", path) + override fun setPath(path: Path) { + require(path.toFile().isDirectory) { + String.format(Locale.US, "path '%s' is not a valid directory", path) } this.path = path for (provider in getProviders()) { if (provider is PathAware) { - (provider as PathAware).setPath(path) + provider.setPath(path) } } } @@ -58,18 +59,17 @@ class PriorityLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, PathAwa return providers } - override fun getLibrarySource(libraryIdentifier: VersionedIdentifier?): InputStream? { + override fun getLibrarySource(libraryIdentifier: VersionedIdentifier): InputStream? { return getLibraryContent(libraryIdentifier, LibraryContentType.CQL) } override fun getLibraryContent( - libraryIdentifier: VersionedIdentifier?, + libraryIdentifier: VersionedIdentifier, type: LibraryContentType ): InputStream? { - validateInput(libraryIdentifier, type) var content: InputStream? for (provider in getProviders()) { - content = provider.getLibraryContent(libraryIdentifier!!, type) + content = provider.getLibraryContent(libraryIdentifier, type) if (content != null) { return content } @@ -83,16 +83,8 @@ class PriorityLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, PathAwa this.namespaceManager = namespaceManager for (provider in getProviders()) { if (provider is NamespaceAware) { - (provider as NamespaceAware).setNamespaceManager(namespaceManager) + provider.setNamespaceManager(namespaceManager) } } } - - private fun validateInput(libraryIdentifier: VersionedIdentifier?, type: LibraryContentType?) { - requireNotNull(type) { "libraryContentType is null." } - requireNotNull(libraryIdentifier) { "libraryIdentifier is null." } - require(!(libraryIdentifier.id == null || libraryIdentifier.id == "")) { - "libraryIdentifier Id is null." - } - } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/StringLibrarySourceProvider.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/StringLibrarySourceProvider.kt index 40772a3c6..cb437f58b 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/StringLibrarySourceProvider.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/StringLibrarySourceProvider.kt @@ -2,6 +2,8 @@ package org.cqframework.cql.cql2elm import java.io.ByteArrayInputStream import java.io.InputStream +import java.util.* +import kotlin.collections.ArrayList import org.hl7.elm.r1.VersionedIdentifier /** @@ -28,8 +30,8 @@ class StringLibrarySourceProvider(private val libraries: List) : Library } if (matches.size > 1) { throw IllegalArgumentException( - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Multiple libraries for id : %s resolved.%nEnsure that there are no duplicates in the input set.", libraryIdentifier.toString() ) diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemFunctionResolver.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemFunctionResolver.kt index 81a0d9d9c..129c0d436 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemFunctionResolver.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemFunctionResolver.kt @@ -2,6 +2,8 @@ package org.cqframework.cql.cql2elm +import java.util.* +import kotlin.collections.ArrayList import org.cqframework.cql.cql2elm.model.Invocation import org.cqframework.cql.cql2elm.model.invocation.* import org.cqframework.cql.cql2elm.model.invocation.DateInvocation.Companion.setDateFieldsFromOperands @@ -10,7 +12,7 @@ import org.cqframework.cql.cql2elm.model.invocation.TimeInvocation.Companion.set import org.cqframework.cql.elm.IdObjectFactory import org.hl7.elm.r1.* -@Suppress("LargeClass", "TooManyFunctions", "ImplicitDefaultLocale") +@Suppress("LargeClass", "TooManyFunctions") class SystemFunctionResolver(private val builder: LibraryBuilder, of: IdObjectFactory?) { private val of = builder.objectFactory @@ -96,9 +98,9 @@ class SystemFunctionResolver(private val builder: LibraryBuilder, of: IdObjectFa // If the op can be converted to both a Date and a DateTime, throw an ambiguous // error if ( - !(op.resultType.isSubTypeOf(builder.resolveTypeName("System", "Date")) || + !(op.resultType.isSubTypeOf(builder.resolveTypeName("System", "Date")!!) || op.resultType.isSubTypeOf( - builder.resolveTypeName("System", "DateTime") + builder.resolveTypeName("System", "DateTime")!! )) ) { val dateConversion = @@ -116,35 +118,39 @@ class SystemFunctionResolver(private val builder: LibraryBuilder, of: IdObjectFa false ) op = - if (dateConversion != null && dateTimeConversion != null) { - if (dateConversion.score == dateTimeConversion.score) { + when { + dateConversion != null && dateTimeConversion != null -> { + require(dateConversion.score != dateTimeConversion.score) { + "Ambiguous implicit conversion from %s to %s or %s." + .format( + Locale.US, + op.resultType.toString(), + dateConversion.toType.toString(), + dateTimeConversion.toType.toString() + ) + } + + if (dateConversion.score < dateTimeConversion.score) { + builder.convertExpression(op, dateConversion) + } else { + builder.convertExpression(op, dateTimeConversion) + } + } + dateConversion != null -> + builder.convertExpression(op, dateConversion) + dateTimeConversion != null -> + builder.convertExpression(op, dateTimeConversion) + else -> { // ERROR throw IllegalArgumentException( String.format( - "Ambiguous implicit conversion from %s to %s or %s.", - op.resultType.toString(), - dateConversion.toType.toString(), - dateTimeConversion.toType.toString() + Locale.US, + "Could not resolve call to operator %s with argument of type %s.", + functionRef.name, + op.resultType.toString() ) ) - } else if (dateConversion.score < dateTimeConversion.score) { - builder.convertExpression(op, dateConversion) - } else { - builder.convertExpression(op, dateTimeConversion) } - } else if (dateConversion != null) { - builder.convertExpression(op, dateConversion) - } else if (dateTimeConversion != null) { - builder.convertExpression(op, dateTimeConversion) - } else { - // ERROR - throw IllegalArgumentException( - String.format( - "Could not resolve call to operator %s with argument of type %s.", - functionRef.name, - op.resultType.toString() - ) - ) } } ops.add(builder.enforceCompatible(patientBirthDateProperty, op.resultType)) @@ -640,6 +646,7 @@ class SystemFunctionResolver(private val builder: LibraryBuilder, of: IdObjectFa else -> throw IllegalArgumentException( String.format( + Locale.US, "Could not resolve call to system operator %s. Unknown conversion type.", functionRef.name ) @@ -656,7 +663,11 @@ class SystemFunctionResolver(private val builder: LibraryBuilder, of: IdObjectFa of.javaClass.getMethod("create" + functionRef.name).invoke(of) as T } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { throw CqlInternalException( - String.format("Could not create instance of Element \"%s\"", functionRef.name), + String.format( + Locale.US, + "Could not create instance of Element \"%s\"", + functionRef.name + ), if (functionRef.trackbacks.isNotEmpty()) functionRef.trackbacks[0] else null, e ) @@ -710,6 +721,7 @@ class SystemFunctionResolver(private val builder: LibraryBuilder, of: IdObjectFa private fun checkNumberOfOperands(functionRef: FunctionRef, expectedOperands: Int) { require(functionRef.operand.size == expectedOperands) { String.format( + Locale.US, "Could not resolve call to system operator %s. Expected %d arguments.", functionRef.name, expectedOperands @@ -718,29 +730,24 @@ class SystemFunctionResolver(private val builder: LibraryBuilder, of: IdObjectFa } companion object { - @Suppress("ReturnCount") private fun resolveAgeRelatedFunctionPrecision( functionRef: FunctionRef ): DateTimePrecision { val name = functionRef.name - if (name.contains("Years")) { - return DateTimePrecision.YEAR - } else if (name.contains("Months")) { - return DateTimePrecision.MONTH - } else if (name.contains("Weeks")) { - return DateTimePrecision.WEEK - } else if (name.contains("Days")) { - return DateTimePrecision.DAY - } else if (name.contains("Hours")) { - return DateTimePrecision.HOUR - } else if (name.contains("Minutes")) { - return DateTimePrecision.MINUTE - } else if (name.contains("Second")) { - return DateTimePrecision.SECOND - } else if (name.contains("Milliseconds")) { - return DateTimePrecision.MILLISECOND + return when { + name.contains("Years") -> DateTimePrecision.YEAR + name.contains("Months") -> DateTimePrecision.MONTH + name.contains("Weeks") -> DateTimePrecision.WEEK + name.contains("Days") -> DateTimePrecision.DAY + name.contains("Hours") -> DateTimePrecision.HOUR + name.contains("Minutes") -> DateTimePrecision.MINUTE + name.contains("Second") -> DateTimePrecision.SECOND + name.contains("Milliseconds") -> DateTimePrecision.MILLISECOND + else -> + throw IllegalArgumentException( + String.format(Locale.US, "Unknown precision '%s'.", name) + ) } - throw IllegalArgumentException(String.format("Unknown precision '%s'.", name)) } } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemMethodResolver.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemMethodResolver.kt index a70c708d7..7e768d68c 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemMethodResolver.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemMethodResolver.kt @@ -40,8 +40,8 @@ class SystemMethodResolver( actualCount = ctx.expression().size } require(actualCount == expectedCount) { - @Suppress("ImplicitDefaultLocale") String.format( + Locale.US, "Expected %s argument for method %s.", Integer.valueOf(expectedCount).toString(), functionName @@ -231,7 +231,7 @@ class SystemMethodResolver( private fun gatherChildTypes( dataType: DataType, recurse: Boolean, - dataTypes: MutableSet + dataTypes: MutableSet ) { if (dataType is ClassType) { for (element in dataType.elements) { @@ -260,7 +260,7 @@ class SystemMethodResolver( gatherChildTypes(elementType, recurse, dataTypes) } } else { - dataTypes.add(builder.resolveTypeName("System.Any")) + dataTypes.add(builder.resolveTypeName("System.Any")!!) } } @@ -300,10 +300,10 @@ class SystemMethodResolver( checkArgumentCount(ctx, functionName, 0) val children = of.createChildren() children.source = target - val dataTypes: MutableSet = HashSet() + val dataTypes: MutableSet = HashSet() gatherChildTypes(target.resultType, false, dataTypes) if (dataTypes.size == 1) { - children.resultType = ListType(dataTypes.toTypedArray()[0]) + children.resultType = ListType(dataTypes.iterator().next()) } else { children.resultType = ListType(ChoiceType(dataTypes)) } @@ -316,7 +316,7 @@ class SystemMethodResolver( elements.add(target) elements.add(argument) val elementType = - builder.ensureCompatibleTypes(target.resultType, argument!!.resultType) + builder.ensureCompatibleTypes(target.resultType, argument!!.resultType)!! val list = of.createList() list.resultType = ListType(elementType) list.element.add(builder.ensureCompatible(target, elementType)) @@ -358,7 +358,7 @@ class SystemMethodResolver( checkArgumentCount(ctx, functionName, 0) val descendents = of.createDescendents() descendents.source = target - val dataTypes: MutableSet = HashSet() + val dataTypes: MutableSet = HashSet() gatherChildTypes(target.resultType, true, dataTypes) if (dataTypes.size == 1) { descendents.resultType = ListType(dataTypes.toTypedArray()[0]) diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/TypeBuilder.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/TypeBuilder.kt index 90cf0052a..c87695451 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/TypeBuilder.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/TypeBuilder.kt @@ -2,7 +2,9 @@ package org.cqframework.cql.cql2elm +import java.util.* import javax.xml.namespace.QName +import kotlin.collections.ArrayList import org.cqframework.cql.cql2elm.model.Model import org.cqframework.cql.elm.IdObjectFactory import org.hl7.cql.model.* @@ -71,8 +73,7 @@ class TypeBuilder(private val of: IdObjectFactory, private val mr: ModelResolver } else -> { throw IllegalArgumentException( - @Suppress("ImplicitDefaultLocale") - String.format("Could not convert type %s to a type specifier.", type) + String.format(Locale.US, "Could not convert type %s to a type specifier.", type) ) } } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/InstantiationContextImpl.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/InstantiationContextImpl.kt index 10ede5681..eb59e055b 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/InstantiationContextImpl.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/InstantiationContextImpl.kt @@ -154,7 +154,7 @@ class InstantiationContextImpl( return result } - override fun getIntervalConversionTargets(callType: DataType): Iterable { + override fun getIntervalConversionTargets(callType: DataType): List { val results = ArrayList() for (c in conversionMap.getConversions(callType)) { if (c.toType is IntervalType) { @@ -202,7 +202,7 @@ class InstantiationContextImpl( return results } - override fun getListConversionTargets(callType: DataType): Iterable { + override fun getListConversionTargets(callType: DataType): List { val results = ArrayList() for (c in conversionMap.getConversions(callType)) { if (c.toType is ListType) { @@ -250,7 +250,7 @@ class InstantiationContextImpl( return results } - override fun getSimpleConversionTargets(callType: DataType): Iterable { + override fun getSimpleConversionTargets(callType: DataType): List { val results = ArrayList() for (c in conversionMap.getConversions(callType)) { if (c.toType is SimpleType) { diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/Model.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/Model.kt index 05b068247..c76e84fb3 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/Model.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/Model.kt @@ -33,7 +33,7 @@ open class Model(val modelInfo: ModelInfo, modelManager: ModelManager?) { for (t in index.values) { if (t is ClassType && t.label != null) { - classIndex[casify(t.label)] = t + classIndex[casify(t.label!!)] = t } if (t is NamedType) { @@ -73,8 +73,8 @@ open class Model(val modelInfo: ModelInfo, modelManager: ModelManager?) { return ModelContext( contextName, - contextType as ClassType?, - if (keyName != null) listOf(keyName) else null, + contextType, + if (keyName != null) listOf(keyName) else emptyList(), null ) } diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ModelImporter.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ModelImporter.kt index 3c52412dd..a2fb966bc 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ModelImporter.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ModelImporter.kt @@ -67,22 +67,19 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { for (requiredModel in modelInfo.requiredModelInfo) { val model = modelManager.resolveModel( - ModelIdentifier() - .withSystem(NamespaceManager.getUriPart(requiredModel.url)) - .withId(requiredModel.name) - .withVersion(requiredModel.version) + ModelIdentifier( + system = NamespaceManager.getUriPart(requiredModel.url), + id = requiredModel.name, + version = requiredModel.version, + ) ) - if (model != null) { - modelIndex[requiredModel.name] = model - } + modelIndex[requiredModel.name] = model } // Ensure System model is registered if (!modelIndex.containsKey("System")) { - val systemModel = modelManager.resolveModel(ModelIdentifier().withId("System")) - if (systemModel != null) { - modelIndex["System"] = systemModel - } + val systemModel = modelManager.resolveModel(ModelIdentifier("System")) + modelIndex["System"] = systemModel } } @@ -122,7 +119,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { val modelContext = ModelContext( c.name, - contextType as ClassType?, + contextType, c.keyElement.split(";".toRegex()).dropLastWhile { it.isEmpty() }, c.birthDateElement ) @@ -139,7 +136,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { val modelContext = ModelContext( contextType.simpleName, - contextType as ClassType?, + contextType, mutableListOf("id"), this.modelInfo.patientBirthDatePropertyName ) @@ -215,7 +212,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { typeSpecifier.pointType, typeSpecifier.pointTypeSpecifier ) - return IntervalType(pointType) + return IntervalType(pointType!!) } if (typeSpecifier is ListTypeSpecifier) { @@ -235,16 +232,16 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { val element = TupleTypeElement( specifierElement.name, - resolveTypeSpecifier(specifierElement.elementType) + resolveTypeSpecifier(specifierElement.elementType)!! ) tupleType.addElement(element) } } if (typeSpecifier is ChoiceTypeSpecifier) { - val choices: MutableList = ArrayList() + val choices: MutableList = ArrayList() for (choice in typeSpecifier.choice) { - val choiceType = resolveTypeSpecifier(choice) + val choiceType = resolveTypeSpecifier(choice)!! choices.add(choiceType) } return ChoiceType(choices) @@ -265,13 +262,13 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { resolveTypeName( typeName.substring(typeName.indexOf('<') + 1, typeName.lastIndexOf('>')) ) - return IntervalType(pointType) + return IntervalType(pointType!!) } else if (typeName.lowercase(Locale.getDefault()).startsWith("list<")) { val elementType = resolveTypeName( typeName.substring(typeName.indexOf('<') + 1, typeName.lastIndexOf('>')) ) - return ListType(elementType) + return ListType(elementType!!) } var result = lookupType(typeName) @@ -378,11 +375,11 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { result = SimpleType( qualifiedTypeName, - resolveTypeNameOrSpecifier(t.baseType, t.baseTypeSpecifier) + resolveTypeNameOrSpecifier(t.baseType, t.baseTypeSpecifier), + t.target ) - result.target = t.target } - resolvedTypes[casify(result!!.name)] = result + resolvedTypes[casify(result.name)] = result } return result @@ -402,13 +399,13 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { ): Collection { val elements: MutableList = ArrayList() for (e in infoElements) { - elements.add(TupleTypeElement(e.name, resolveTypeNameOrSpecifier(e))) + elements.add(TupleTypeElement(e.name, resolveTypeNameOrSpecifier(e)!!)) } return elements } private fun resolveTupleType(t: TupleTypeInfo): TupleType { - val result = TupleType(resolveTupleTypeElements(t.element)) + val result = TupleType(resolveTupleTypeElements(t.element).toMutableList()) return result } @@ -434,30 +431,32 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { val genericParameters: MutableList = ArrayList() for (parameterInfo in parameterInfoList) { val constraint = parameterInfo.constraint - var typeConstraint: TypeParameterConstraint? = null - when { - constraint.equals(TypeParameterConstraint.NONE.name, ignoreCase = true) -> { - typeConstraint = TypeParameterConstraint.NONE - } - constraint.equals(TypeParameterConstraint.CLASS.name, ignoreCase = true) -> { - typeConstraint = TypeParameterConstraint.CLASS - } - constraint.equals(TypeParameterConstraint.TUPLE.name, ignoreCase = true) -> { - typeConstraint = TypeParameterConstraint.TUPLE - } - constraint.equals(TypeParameterConstraint.VALUE.name, ignoreCase = true) -> { - typeConstraint = TypeParameterConstraint.VALUE - } - constraint.equals(TypeParameterConstraint.CHOICE.name, ignoreCase = true) -> { - typeConstraint = TypeParameterConstraint.CHOICE - } - constraint.equals(TypeParameterConstraint.INTERVAL.name, ignoreCase = true) -> { - typeConstraint = TypeParameterConstraint.INTERVAL - } - constraint.equals(TypeParameterConstraint.TYPE.name, ignoreCase = true) -> { - typeConstraint = TypeParameterConstraint.TYPE + val typeConstraint = + when { + constraint.equals(TypeParameterConstraint.NONE.name, ignoreCase = true) -> { + TypeParameterConstraint.NONE + } + constraint.equals(TypeParameterConstraint.CLASS.name, ignoreCase = true) -> { + TypeParameterConstraint.CLASS + } + constraint.equals(TypeParameterConstraint.TUPLE.name, ignoreCase = true) -> { + TypeParameterConstraint.TUPLE + } + constraint.equals(TypeParameterConstraint.VALUE.name, ignoreCase = true) -> { + TypeParameterConstraint.VALUE + } + constraint.equals(TypeParameterConstraint.CHOICE.name, ignoreCase = true) -> { + TypeParameterConstraint.CHOICE + } + constraint.equals(TypeParameterConstraint.INTERVAL.name, ignoreCase = true) -> { + TypeParameterConstraint.INTERVAL + } + constraint.equals(TypeParameterConstraint.TYPE.name, ignoreCase = true) -> { + TypeParameterConstraint.TYPE + } + else -> TypeParameterConstraint.NONE } - } + genericParameters.add( TypeParameter( parameterInfo.name, @@ -496,7 +495,13 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { elementType = resolveTypeName("System.Any") } elements.add( - ClassTypeElement(e.name, elementType, e.isProhibited, e.isOneBased, e.target) + ClassTypeElement( + e.name, + elementType!!, + e.isProhibited ?: false, + e.isOneBased ?: false, + e.target + ) ) } return elements @@ -581,7 +586,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { } private fun resolveClassTypeSearch(t: ClassType?, s: SearchInfo): SearchType { - return SearchType(s.name, s.path, resolveTypeNameOrSpecifier(s.type, s.typeSpecifier)) + return SearchType(s.name, s.path, resolveTypeNameOrSpecifier(s.type, s.typeSpecifier)!!) } private fun resolveClassType(t: ClassInfo): ClassType { @@ -684,24 +689,24 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { } private fun resolveIntervalType(t: IntervalTypeInfo): IntervalType { - val result = IntervalType(resolveTypeNameOrSpecifier(t.pointType, t.pointTypeSpecifier)) + val result = IntervalType(resolveTypeNameOrSpecifier(t.pointType, t.pointTypeSpecifier)!!) return result } private fun resolveListType(t: ListTypeInfo): ListType { - val result = ListType(resolveTypeNameOrSpecifier(t.elementType, t.elementTypeSpecifier)) + val result = ListType(resolveTypeNameOrSpecifier(t.elementType, t.elementTypeSpecifier)!!) return result } private fun resolveChoiceType(t: ChoiceTypeInfo): ChoiceType { - val types = ArrayList() + val types = ArrayList() if (t.choice != null && t.choice.isNotEmpty()) { for (typeSpecifier in t.choice) { - types.add(resolveTypeSpecifier(typeSpecifier)) + types.add(resolveTypeSpecifier(typeSpecifier)!!) } } else { for (typeSpecifier in t.type) { - types.add(resolveTypeSpecifier(typeSpecifier)) + types.add(resolveTypeSpecifier(typeSpecifier)!!) } } return ChoiceType(types) @@ -763,9 +768,8 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { * @param type * @return True if the parent of class 'type' is a generic class. */ - fun isParentGeneric(type: ClassType?): Boolean { - val baseType = type!!.baseType - return baseType != null && baseType is ClassType && baseType.isGeneric + fun isParentGeneric(type: ClassType): Boolean { + return type.baseType is ClassType && type.baseType.isGeneric } /** @@ -777,7 +781,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { * @return */ private fun handleGenericType(genericSignature: String, baseType: String): ClassType { - val parser = GenericClassSignatureParser(genericSignature, baseType, null, resolvedTypes) + val parser = GenericClassSignatureParser(genericSignature, baseType, resolvedTypes) val genericClassType = parser.parseGenericSignature() return genericClassType diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ResolvedIdentifierContext.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ResolvedIdentifierContext.kt index b340c97c7..322c97773 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ResolvedIdentifierContext.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ResolvedIdentifierContext.kt @@ -17,6 +17,7 @@ import org.hl7.elm.r1.ValueSetDef * well as the type of matching done to retrieve the element, whether case-sensitive or * case-insensitive. */ +@ExposedCopyVisibility data class ResolvedIdentifierContext private constructor( private val identifier: String, diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessor.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessor.kt index 912085f64..d84a8ad83 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessor.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessor.kt @@ -40,7 +40,8 @@ class CqlPreprocessor(libraryBuilder: LibraryBuilder, tokenStream: TokenStream) val identifier = VersionedIdentifier().withId(libraryInfo.libraryName).withVersion(libraryInfo.version) if (libraryInfo.namespaceName != null) { - identifier.system = libraryBuilder.resolveNamespaceUri(libraryInfo.namespaceName, true) + identifier.system = + libraryBuilder.resolveNamespaceUri(libraryInfo.namespaceName!!, true) } else if (libraryBuilder.namespaceInfo != null) { identifier.system = libraryBuilder.namespaceInfo.uri } @@ -127,7 +128,7 @@ class CqlPreprocessor(libraryBuilder: LibraryBuilder, tokenStream: TokenStream) var modelNamespace: NamespaceInfo? = null if (namespaceName != null) { val namespaceUri = libraryBuilder.resolveNamespaceUri(namespaceName, true) - modelNamespace = NamespaceInfo(namespaceName, namespaceUri) + modelNamespace = NamespaceInfo(namespaceName, namespaceUri!!) } val localIdentifier = if (ctx.localIdentifier() == null) unqualifiedIdentifier diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessorElmCommonVisitor.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessorElmCommonVisitor.kt index e7c73527c..241a59c01 100644 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessorElmCommonVisitor.kt +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/preprocessor/CqlPreprocessorElmCommonVisitor.kt @@ -208,16 +208,15 @@ abstract class CqlPreprocessorElmCommonVisitor( libraryBuilder.checkCompatibilityLevel("Fluent functions", "1.5") functionDef.isFluent = true } - if (ctx.operandDefinition() != null) { - for (opdef in ctx.operandDefinition()) { - val typeSpecifier = parseTypeSpecifier(opdef.typeSpecifier())!! - functionDef.operand.add( - of.createOperandDef() - .withName(parseString(opdef.referentialIdentifier())) - .withOperandTypeSpecifier(typeSpecifier) - .withResultType(typeSpecifier.resultType) as OperandDef - ) - } + + for (opdef in ctx.operandDefinition()) { + val typeSpecifier = parseTypeSpecifier(opdef.typeSpecifier())!! + functionDef.operand.add( + of.createOperandDef() + .withName(parseString(opdef.referentialIdentifier())) + .withOperandTypeSpecifier(typeSpecifier) + .withResultType(typeSpecifier.resultType) as OperandDef + ) } val typeSpecifierContext = ctx.typeSpecifier() return if (typeSpecifierContext == null) { @@ -237,11 +236,9 @@ abstract class CqlPreprocessorElmCommonVisitor( protected fun parseQualifiers(ctx: NamedTypeSpecifierContext): List { val qualifiers = ArrayList() - if (ctx.qualifier() != null) { - for (qualifierContext in ctx.qualifier()) { - val qualifier = parseString(qualifierContext)!! - qualifiers.add(qualifier) - } + for (qualifierContext in ctx.qualifier()) { + val qualifier = parseString(qualifierContext)!! + qualifiers.add(qualifier) } return qualifiers } @@ -255,10 +252,7 @@ abstract class CqlPreprocessorElmCommonVisitor( val modelId = modelName ?: libraryInfo.defaultUsingDefinition?.name val modelVersion = version ?: libraryInfo.defaultUsingDefinition?.version val modelIdentifier = - ModelIdentifier() - .withId(modelId) - .withVersion(modelVersion) - .withSystem(modelNamespace?.uri) + ModelIdentifier(id = modelId!!, version = modelVersion, system = modelNamespace?.uri) return libraryBuilder.getModel(modelIdentifier, localIdentifier) } diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/TypeResolver.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/TypeResolver.java index d64849c66..761ec823d 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/TypeResolver.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/TypeResolver.java @@ -136,7 +136,7 @@ private DataType resolveTupleTypeSpecifier(TupleTypeSpecifier typeSpecifier) { TupleType tupleType = new TupleType(); for (TupleElementDefinition element : typeSpecifier.getElement()) { TupleTypeElement tupleElement = - new TupleTypeElement(element.getName(), resolveTypeSpecifier(element.getElementType())); + new TupleTypeElement(element.getName(), resolveTypeSpecifier(element.getElementType()), false); tupleType.addElement(tupleElement); } return tupleType; @@ -151,7 +151,7 @@ private DataType resolveListTypeSpecifier(ListTypeSpecifier typeSpecifier) { } private DataType resolveChoiceTypeSpecifier(ChoiceTypeSpecifier typeSpecifier) { - ArrayList choiceTypes = new ArrayList(); + var choiceTypes = new ArrayList(); for (TypeSpecifier choiceType : typeSpecifier.getChoice()) { choiceTypes.add(resolveTypeSpecifier(choiceType)); } 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 f077679e5..3f9aeefd1 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 @@ -1930,10 +1930,14 @@ private void assertEqualToExpectedModuleDefinitionLibrary( (org.hl7.fhir.r5.model.Library) parser.parseResource( DataRequirementsProcessorTest.class.getResourceAsStream(pathToExpectedModuleDefinitionLibrary)); assertNotNull(expectedModuleDefinitionLibrary); - // outputModuleDefinitionLibrary(actualModuleDefinitionLibrary); actualModuleDefinitionLibrary.setDate(null); expectedModuleDefinitionLibrary.setDate(null); - assertTrue(actualModuleDefinitionLibrary.equalsDeep(expectedModuleDefinitionLibrary)); + + parser.setPrettyPrint(true); + var jsonExpected = parser.encodeResourceToString(expectedModuleDefinitionLibrary); + var jsonActual = parser.encodeResourceToString(actualModuleDefinitionLibrary); + + assertEquals(jsonExpected, jsonActual); } @Test 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 cd3f5b5a7..e62fa9a0d 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 @@ -65,7 +65,7 @@ "dateFilter": [ { "path": "effective", "valuePeriod": { - "start": "2022-07-20T00:00:00Z", + "start": "2022-07-20T00:00:00.000Z", "end": "2023-01-15T23:59:59.999Z" } } ] @@ -85,7 +85,7 @@ "dateFilter": [ { "path": "effective", "valuePeriod": { - "start": "2022-07-20T00:00:00Z", + "start": "2022-07-20T00:00:00.000Z", "end": "2023-01-15T23:59:59.999Z" } } ] @@ -105,7 +105,7 @@ "dateFilter": [ { "path": "effective", "valuePeriod": { - "start": "2022-01-16T00:00:00Z", + "start": "2022-01-16T00:00:00.000Z", "end": "2023-01-15T23:59:59.999Z" } } ] @@ -125,7 +125,7 @@ "dateFilter": [ { "path": "effective", "valuePeriod": { - "start": "2022-07-20T00:00:00Z", + "start": "2022-07-20T00:00:00.000Z", "end": "2023-01-15T23:59:59.999Z" } } ] @@ -145,7 +145,7 @@ "dateFilter": [ { "path": "effective", "valuePeriod": { - "start": "2022-01-16T00:00:00Z", + "start": "2022-01-16T00:00:00.000Z", "end": "2023-01-15T23:59:59.999Z" } } ] diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu2ModelResolver.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu2ModelResolver.java index d5132fce6..e0656cbfa 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu2ModelResolver.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu2ModelResolver.java @@ -67,7 +67,7 @@ void resolverThrowsExceptionForUnknownType() { void resolveModelInfoTests() { ModelResolver resolver = new Dstu2FhirModelResolver(); ModelManager mm = new ModelManager(); - Model m = mm.resolveModel(new ModelIdentifier().withId("FHIR").withVersion("1.0.2")); + Model m = mm.resolveModel(new ModelIdentifier("FHIR", null, "1.0.2")); List typeInfos = m.getModelInfo().getTypeInfo(); diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu3ModelResolver.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu3ModelResolver.java index 8d78a00d1..7262b6c51 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu3ModelResolver.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestDstu3ModelResolver.java @@ -115,7 +115,7 @@ void resolveTypeTests() { void resolveModelInfoTests() { ModelResolver resolver = new Dstu3FhirModelResolver(FhirContext.forCached(FhirVersionEnum.DSTU3)); ModelManager mm = new ModelManager(); - Model m = mm.resolveModel(new ModelIdentifier().withId("FHIR").withVersion("3.0.0")); + Model m = mm.resolveModel(new ModelIdentifier("FHIR", null, "3.0.0")); List typeInfos = m.getModelInfo().getTypeInfo(); diff --git a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestR4ModelResolver.java b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestR4ModelResolver.java index 13a1d8da5..911bb3b59 100644 --- a/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestR4ModelResolver.java +++ b/Src/java/engine-fhir/src/test/java/org/opencds/cqf/cql/engine/fhir/model/TestR4ModelResolver.java @@ -162,7 +162,7 @@ void modelInfoSpecialCaseTests() { void modelInfo400Tests() { ModelResolver resolver = new R4FhirModelResolver(FhirContext.forCached(FhirVersionEnum.R4)); ModelManager mm = new ModelManager(); - Model m = mm.resolveModel(new ModelIdentifier().withId("FHIR").withVersion("4.0.1")); + Model m = mm.resolveModel(new ModelIdentifier("FHIR", null, "4.0.1")); List typeInfos = m.getModelInfo().getTypeInfo(); @@ -198,7 +198,7 @@ void modelInfo400Tests() { void modelInfo401Tests() throws Exception { ModelResolver resolver = new R4FhirModelResolver(FhirContext.forCached(FhirVersionEnum.R4)); ModelManager mm = new ModelManager(); - Model m = mm.resolveModel(new ModelIdentifier().withId("FHIR").withVersion("4.0.1")); + Model m = mm.resolveModel(new ModelIdentifier("FHIR", null, "4.0.1")); List typeInfos = m.getModelInfo().getTypeInfo(); diff --git a/Src/java/model/build.gradle.kts b/Src/java/model/build.gradle.kts index e0ef4e153..cb5ec6ba0 100644 --- a/Src/java/model/build.gradle.kts +++ b/Src/java/model/build.gradle.kts @@ -3,10 +3,6 @@ plugins { id("cql.xjc-conventions") } -dependencies { - implementation("org.apache.commons:commons-text:1.10.0") -} - tasks.register("generateModel") { schema = "${projectDir}/../../cql-lm/schema/model/modelinfo.xsd" extraArgs = listOf("-npa") diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.kt new file mode 100644 index 000000000..754b6cd33 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.kt @@ -0,0 +1,62 @@ +package org.hl7.cql.model + +abstract class BaseDataType protected constructor(baseType: DataType? = DataType.ANY) : DataType { + override val baseType: DataType = baseType ?: DataType.ANY + + override fun toLabel(): String { + return toString() + } + + override fun isSubTypeOf(other: DataType): Boolean { + var currentType: DataType = this + while (currentType != DataType.ANY) { + if (currentType == other) { + return true + } + currentType = currentType.baseType + } + + return currentType == other + } + + override fun isSuperTypeOf(other: DataType): Boolean { + var currentType = other + while (currentType != DataType.ANY) { + if (this == currentType) { + return true + } + currentType = currentType.baseType + } + + return this == currentType + } + + /** + * @param other + * @return The first supertype of this type that is also a supertype of other + */ + override fun getCommonSuperTypeOf(other: DataType): DataType { + var currentType: DataType = this + while (currentType != DataType.ANY) { + if (currentType.isSuperTypeOf(other)) { + return currentType + } + currentType = currentType.baseType + } + + return DataType.ANY + } + + // Note that this is not how implicit/explicit conversions are defined, the notion of + // type compatibility is used to support implicit casting, such as casting a "null" + // literal to any other type, or casting a class to an equivalent tuple. + override fun isCompatibleWith(other: DataType): Boolean { + return when { + // Any data type is compatible with itself + this == other -> true + // A type is compatible with a choice type if it is a subtype of one of the choice types + other is ChoiceType -> other.types.any { this.isSubTypeOf(it) } + else -> false + } + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.java b/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.java deleted file mode 100644 index 5b43a3f0b..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.hl7.cql.model; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Bryn on 11/8/2016. - */ -public class ChoiceType extends DataType { - - public ChoiceType(Iterable types) { - // Expand choice types in the constructor, it never makes sense to have a choice of choices - for (DataType type : types) { - addType(type); - } - } - - private ArrayList types = new ArrayList<>(); - - public Iterable getTypes() { - return types; - } - - private void addType(DataType type) { - if (type instanceof ChoiceType) { - ChoiceType choiceType = (ChoiceType) type; - for (DataType choice : choiceType.getTypes()) { - addType(choice); - } - } else { - types.add(type); - } - } - - @Override - public int hashCode() { - int result = 13; - for (int i = 0; i < types.size(); i++) { - result += (37 * types.get(i).hashCode()); - } - - return result; - } - - @Override - public boolean equals(Object o) { - if (o instanceof ChoiceType) { - ChoiceType that = (ChoiceType) o; - - if (this.types.size() == that.types.size()) { - List theseTypes = this.types; - List thoseTypes = that.types; - for (int i = 0; i < theseTypes.size(); i++) { - if (!theseTypes.get(i).equals(thoseTypes.get(i))) { - return false; - } - } - - return true; - } - } - - return false; - } - - @Override - public boolean isSubTypeOf(DataType other) { - // Choice types do not follow the is-a relationship, they use the is-compatible relationship instead, defined - // using subset/superset - return super.isSubTypeOf(other); - } - - public boolean isSubSetOf(ChoiceType other) { - for (DataType type : types) { - Boolean currentIsSubType = false; - for (DataType otherType : other.types) { - currentIsSubType = type.isSubTypeOf(otherType); - if (currentIsSubType) { - break; - } - } - - if (!currentIsSubType) { - return false; - } - } - - return true; - } - - @Override - public boolean isSuperTypeOf(DataType other) { - return super.isSuperTypeOf(other); - } - - public boolean isSuperSetOf(ChoiceType other) { - return other.isSubSetOf(this); - } - - @Override - public boolean isCompatibleWith(DataType other) { - // This type is compatible with the other type if - // The other type is a subtype of one of the choice types - // The other type is a choice type and all the components of this choice are a subtype of some component of the - // other type - if (other instanceof ChoiceType) { - return this.isSubSetOf((ChoiceType) other) || this.isSuperSetOf((ChoiceType) other); - } - - for (DataType type : types) { - if (other.isCompatibleWith(type)) { - return true; - } - } - - return super.isCompatibleWith(other); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("choice<"); - boolean first = true; - for (DataType type : types) { - if (first) { - first = false; - } else { - sb.append(","); - } - sb.append(type.toString()); - } - sb.append(">"); - return sb.toString(); - } - - @Override - public boolean isGeneric() { - // TODO: It hardly makes sense for a choice type to have generics.... ignoring in instantiation semantics for - // now - for (DataType type : types) { - if (type.isGeneric()) { - return true; - } - } - - return false; - } - - @Override - public boolean isInstantiable(DataType callType, InstantiationContext context) { - // Call isInstantiable recursively to make sure that type parameters (if present) are bound - if (callType.equals(DataType.ANY)) { - for (var type : types) { - if (!type.isInstantiable(callType, context)) { - return false; - } - } - return true; - } - - return isSuperTypeOf(callType); - } - - @Override - public DataType instantiate(InstantiationContext context) { - return this; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.kt new file mode 100644 index 000000000..bda8b1bd0 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.kt @@ -0,0 +1,63 @@ +package org.hl7.cql.model + +import org.hl7.cql.model.DataType.Companion.ANY + +@ConsistentCopyVisibility +/** Created by Bryn on 11/8/2016. */ +data class ChoiceType private constructor(val types: Set) : BaseDataType() { + constructor(types: Iterable) : this(types.flattenChoices()) + + constructor(vararg types: DataType) : this(types.toList().flattenChoices()) + + init { + require(types.isNotEmpty()) { "A choice type must have at least one type." } + require(types.none { it is ChoiceType }) { + "A choice type cannot contain another choice type." + } + } + + fun isSubSetOf(other: ChoiceType): Boolean { + // every type in this choice is a subtype of some type in the other choice + return types.all { x -> other.types.any { x.isSubTypeOf(it) } } + } + + fun isSuperSetOf(other: ChoiceType): Boolean { + return other.isSubSetOf(this) + } + + override fun isCompatibleWith(other: DataType): Boolean { + // This type is compatible with the other type if + // The other type is a subtype of one of the choice types + // The other type is a choice type and all the components of this choice are a subtype of + // some component of the + // other type + return when { + other is ChoiceType -> this.isSubSetOf(other) || this.isSuperSetOf(other) + types.any { other.isCompatibleWith(it) } -> true + else -> super.isCompatibleWith(other) + } + } + + override fun toString(): String { + return types.joinToString(",", "choice<", ">") + } + + override val isGeneric: Boolean = types.any { it.isGeneric } + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + // Call isInstantiable recursively to make sure that type parameters (if present) are bound + if (callType == ANY) { + return types.any { !it.isInstantiable(callType, context) } + } + return isSuperTypeOf(callType) + } + + override fun instantiate(context: InstantiationContext): DataType { + return this + } + + companion object { + private fun Iterable.flattenChoices(): Set = + flatMap { (it as? ChoiceType)?.types ?: setOf(it) }.toSet() + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ClassType.java b/Src/java/model/src/main/java/org/hl7/cql/model/ClassType.java deleted file mode 100644 index fbe2ff11a..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ClassType.java +++ /dev/null @@ -1,473 +0,0 @@ -package org.hl7.cql.model; - -import java.util.*; - -public class ClassType extends DataType implements NamedType { - - public ClassType( - String name, - DataType baseType, - Collection elements, - Collection parameters) { - super(baseType); - - if (name == null || name.equals("")) { - throw new IllegalArgumentException("name is null"); - } - - this.name = name; - - if (parameters != null) { - this.genericParameters.addAll(parameters); - } - - if (elements != null) { - this.elements.addAll(elements); - } - } - - public ClassType() { - this(null, null, null, null); - } - - public ClassType(String name) { - this(name, null, null, null); - } - - public ClassType(String name, DataType baseType) { - this(name, baseType, null, null); - } - - public ClassType(String name, DataType baseType, Collection elements) { - this(name, baseType, elements, null); - } - - private String name; - - public String getName() { - return this.name; - } - - public String getNamespace() { - if (this.name != null) { - int qualifierIndex = this.name.indexOf( - '.'); // TODO Should this not be the last occurrence rather than the first occurrence? - if (qualifierIndex > 0) { - return this.name.substring(0, qualifierIndex); - } - } - - return ""; - } - - public String getSimpleName() { - if (this.name != null) { - int qualifierIndex = this.name.indexOf( - '.'); // TODO Should this not be the last occurrence rather than the first occurrence? - if (qualifierIndex > 0) { - return this.name.substring(qualifierIndex + 1); - } - } - - return this.name; - } - - private String identifier; - - public String getIdentifier() { - return identifier; - } - - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - private String label; - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - private String target; - - public String getTarget() { - return target; - } - - public void setTarget(String target) { - this.target = target; - } - - private boolean retrievable; - - public boolean isRetrievable() { - return retrievable; - } - - public void setRetrievable(boolean retrievable) { - this.retrievable = retrievable; - } - - private String primaryCodePath; - - public String getPrimaryCodePath() { - return primaryCodePath; - } - - public void setPrimaryCodePath(String primaryCodePath) { - this.primaryCodePath = primaryCodePath; - } - - private String primaryValueSetPath; - - public String getPrimaryValueSetPath() { - return primaryValueSetPath; - } - - public void setPrimaryValueSetPath(String primaryValueSetPath) { - this.primaryValueSetPath = primaryValueSetPath; - } - - private List relationships = new ArrayList<>(); - - public Iterable getRelationships() { - return relationships; - } - - public void addRelationship(Relationship relationship) { - relationships.add(relationship); - } - - private List targetRelationships = new ArrayList<>(); - - public Iterable getTargetRelationships() { - return targetRelationships; - } - - public void addTargetRelationship(Relationship relationship) { - targetRelationships.add(relationship); - } - - private List searches = new ArrayList<>(); - - public Iterable getSearches() { - return searches; - } - - public void addSearch(SearchType search) { - searches.add(search); - } - - public SearchType findSearch(String searchPath) { - if (searches != null) { - for (SearchType search : searches) { - if (search.getName().equals(searchPath)) { - return search; - } - } - } - - return null; - } - - /** - * Generic class parameters such 'S', 'T extends MyType'. - */ - private List genericParameters = new ArrayList<>(); - - /** - * Returns the generic parameters for the generic type. For instance, - * for the generic type Map<K,V extends Person>, two generic parameters - * will be returned: K and V extends Person. The latter parameter has a constraint - * restricting the type of the bound type to be a valid subtype of Person. - * - * @return Class' generic parameters - */ - public List getGenericParameters() { - return genericParameters; - } - - /** - * Sets the generic parameters for the generic type. For instance, - * for the generic type Map<K,V extends Person>, two generic parameters - * should be set: K and V extends Person. The latter parameter has a constraint - * restricting the type of the bound type to be a valid subtype of Person. - * - * @param genericParameters - */ - public void setGenericParameters(List genericParameters) { - this.genericParameters = genericParameters; - } - - /** - * Adds a parameter declaration to the generic type. - * - * @param genericParameter - */ - public void addGenericParameter(TypeParameter genericParameter) { - this.genericParameters.add(genericParameter); - } - - /** - * Adds collection of type parameters to existing set. - * @param parameters - */ - public void addGenericParameter(Collection parameters) { - for (TypeParameter parameter : parameters) { - internalAddParameter(parameter); - } - - sortedElements = null; - tupleType = null; - } - - /** - * Returns the parameter with the given parameter identifier. - * If not found in the given class, it looks in the parent class. - * - * @param identifier - * @return Generic parameter with the given name in the current class or in the base class. Null if none found. - */ - public TypeParameter getGenericParameterByIdentifier(String identifier) { - return getGenericParameterByIdentifier(identifier, false); - } - - /** - * Returns the parameter with the given parameter identifier. - * If inCurrentClassOnly is false, if not found in the given class, then it looks in the parent class. - * If inCurrentClassOnly is true, only looks for parameter in the given class. - * - * @param identifier - * @param inCurrentClassOnly - * @return Class' generic parameter - */ - public TypeParameter getGenericParameterByIdentifier(String identifier, boolean inCurrentClassOnly) { - TypeParameter param = null; - for (TypeParameter genericParameter : genericParameters) { - if (identifier.equalsIgnoreCase(genericParameter.getIdentifier())) { - param = genericParameter; - break; - } - } - if (!inCurrentClassOnly && param == null) { - if (param == null && getBaseType() instanceof ClassType) { - param = ((ClassType) getBaseType()).getGenericParameterByIdentifier(identifier); - } - } - return param; - } - - private List elements = new ArrayList(); - private List sortedElements = null; - private LinkedHashMap baseElementMap = null; - - public List getElements() { - return elements; - } - - private LinkedHashMap getBaseElementMap() { - if (baseElementMap == null) { - baseElementMap = new LinkedHashMap<>(); - if (getBaseType() instanceof ClassType) { - ((ClassType) getBaseType()).gatherElements(baseElementMap); - } - } - - return baseElementMap; - } - - private void gatherElements(LinkedHashMap elementMap) { - if (getBaseType() instanceof ClassType) { - ((ClassType) getBaseType()).gatherElements(elementMap); - } - - for (ClassTypeElement element : elements) { - elementMap.put(element.getName(), element); - } - } - - public List getAllElements() { - // Get the baseClass elements into a map by name - LinkedHashMap elementMap = new LinkedHashMap<>(getBaseElementMap()); - - // Add this class's elements, overwriting baseClass definitions where applicable - for (ClassTypeElement el : elements) { - elementMap.put(el.getName(), el); - } - - return new ArrayList<>(elementMap.values()); - } - - private void internalAddElement(ClassTypeElement element) { - ClassTypeElement existingElement = getBaseElementMap().get(element.getName()); - if (existingElement != null - && !(existingElement.getType() instanceof TypeParameter) - && (!(element.getType().isSubTypeOf(existingElement.getType()) - || (existingElement.getType() instanceof ListType - && element.getType() - .isSubTypeOf(((ListType) existingElement.getType()).getElementType())) - || (existingElement.getType() instanceof IntervalType - && element.getType() - .isSubTypeOf(((IntervalType) existingElement.getType()).getPointType())) - || (existingElement.getType() instanceof ChoiceType - && element.getType().isCompatibleWith(existingElement.getType()))))) { - throw new InvalidRedeclarationException(this, existingElement, element); - } - - this.elements.add(element); - } - - private void internalAddParameter(TypeParameter parameter) { - // TODO Flesh out and retain method only if needed. - - this.genericParameters.add(parameter); - } - - public void addElement(ClassTypeElement element) { - internalAddElement(element); - sortedElements = null; - tupleType = null; - } - - public void addElements(Collection elements) { - for (ClassTypeElement element : elements) { - internalAddElement(element); - } - - sortedElements = null; - tupleType = null; - } - - private List getSortedElements() { - if (sortedElements == null) { - sortedElements = new ArrayList<>(elements); - Collections.sort(sortedElements, (left, right) -> left.getName().compareTo(right.getName())); - } - - return sortedElements; - } - - @Override - public int hashCode() { - return this.name.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof ClassType) { - ClassType that = (ClassType) o; - return this.name.equals(that.name); - } - - return false; - } - - @Override - public String toString() { - return this.name; - } - - @Override - public String toLabel() { - return this.label == null ? this.name : this.label; - } - - private TupleType tupleType; - - public TupleType getTupleType() { - if (tupleType == null) { - tupleType = buildTupleType(); - } - - return tupleType; - } - - private void addTupleElements(ClassType classType, LinkedHashMap elements) { - // Add base elements first - DataType baseType = classType.getBaseType(); - if (baseType instanceof ClassType) { - addTupleElements((ClassType) baseType, elements); - } - - for (ClassTypeElement element : classType.getElements()) { - if (!element.isProhibited()) { - TupleTypeElement tupleElement = new TupleTypeElement(element.getName(), element.getType()); - elements.put(tupleElement.getName(), tupleElement); - } - } - } - - private TupleType buildTupleType() { - LinkedHashMap tupleElements = new LinkedHashMap<>(); - - addTupleElements(this, tupleElements); - - return new TupleType(tupleElements.values()); - } - - @Override - public boolean isCompatibleWith(DataType other) { - if (other instanceof TupleType) { - TupleType tupleType = (TupleType) other; - return getTupleType().equals(tupleType); - // Github #115: It's incorrect for a class type to be considered compatible with another class type on the - // basis of the inferred tuple type alone. - // } else if (other instanceof ClassType) { - // ClassType classType = (ClassType)other; - // return getTupleType().equals(classType.getTupleType()); - } - - return super.isCompatibleWith(other); - } - - @Override - public boolean isGeneric() { - return genericParameters != null && genericParameters.size() > 0; - } - - @Override - public boolean isInstantiable(DataType callType, InstantiationContext context) { - if (callType instanceof ClassType) { - ClassType classType = (ClassType) callType; - if (elements.size() == classType.elements.size()) { - List theseElements = getSortedElements(); - List thoseElements = classType.getSortedElements(); - for (int i = 0; i < theseElements.size(); i++) { - if (!(theseElements - .get(i) - .getName() - .equals(thoseElements.get(i).getName()) - && theseElements - .get(i) - .getType() - .isInstantiable(thoseElements.get(i).getType(), context))) { - return false; - } - } - - return true; - } - } - - return false; - } - - @Override - public DataType instantiate(InstantiationContext context) { - if (!isGeneric()) { - return this; - } - - ClassType result = new ClassType(getName(), getBaseType()); - for (int i = 0; i < elements.size(); i++) { - result.addElement(new ClassTypeElement( - elements.get(i).getName(), elements.get(i).getType().instantiate(context))); - } - - return result; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ClassType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ClassType.kt new file mode 100644 index 000000000..4e818c3d5 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ClassType.kt @@ -0,0 +1,298 @@ +package org.hl7.cql.model + +import java.util.* + +@Suppress("TooManyFunctions", "ComplexCondition") +open class ClassType +@JvmOverloads +constructor( + final override val name: String, + baseType: DataType? = null, + val elements: MutableList = mutableListOf(), + /** + * Generic class parameters such 'S', 'T extends MyType'. + * + * For instance, for the generic type Map<K,V extends Person>, two generic parameters will + * be returned: K and V extends Person. The latter parameter has a constraint restricting the + * type of the bound type to be a valid subtype of Person. + */ + var genericParameters: MutableList = mutableListOf() +) : BaseDataType(baseType), NamedType { + init { + require(name.isNotEmpty()) { "A class type must have a name." } + } + + override val namespace: String + get() { + // TODO Should this not be the last occurrence rather than the first occurrence? + val qualifierIndex = name.indexOf('.') + return if (qualifierIndex > 0) { + name.substring(0, qualifierIndex) + } else "" + } + + override val simpleName: String + get() { + // TODO Should this not be the last occurrence rather than the first occurrence? + val qualifierIndex = name.indexOf('.') + return if (qualifierIndex > 0) { + name.substring(qualifierIndex + 1) + } else name + } + + var identifier: String? = null + var label: String? = null + override var target: String? = null + var isRetrievable: Boolean = false + + var primaryCodePath: String? = null + + var primaryValueSetPath: String? = null + + private val relationships: MutableList = ArrayList() + + fun getRelationships(): List { + return relationships + } + + fun addRelationship(relationship: Relationship) { + relationships.add(relationship) + } + + private val targetRelationships: MutableList = ArrayList() + + fun getTargetRelationships(): List { + return targetRelationships + } + + fun addTargetRelationship(relationship: Relationship) { + targetRelationships.add(relationship) + } + + private val searches: MutableList = ArrayList() + + fun getSearches(): List { + return searches + } + + fun addSearch(search: SearchType) { + searches.add(search) + } + + fun findSearch(searchPath: String): SearchType? { + return searches.firstOrNull() { it.name == searchPath } + } + + /** + * Adds a parameter declaration to the generic type. + * + * @param genericParameter + */ + fun addGenericParameter(genericParameter: TypeParameter) { + genericParameters.add(genericParameter) + } + + /** + * Adds collection of type parameters to existing set. + * + * @param parameters + */ + fun addGenericParameter(parameters: Collection) { + for (parameter in parameters) { + internalAddParameter(parameter) + } + } + + /** + * Returns the parameter with the given parameter identifier. If not found in the given class, + * it looks in the parent class. + * + * @param identifier + * @return Generic parameter with the given name in the current class or in the base class. Null + * if none found. + */ + fun getGenericParameterByIdentifier(identifier: String): TypeParameter? { + return getGenericParameterByIdentifier(identifier, false) + } + + /** + * Returns the parameter with the given parameter identifier. If inCurrentClassOnly is false, if + * not found in the given class, then it looks in the parent class. If inCurrentClassOnly is + * true, only looks for parameter in the given class. + * + * @param identifier + * @param inCurrentClassOnly + * @return Class' generic parameter + */ + fun getGenericParameterByIdentifier( + identifier: String, + inCurrentClassOnly: Boolean + ): TypeParameter? { + var param: TypeParameter? = null + for (genericParameter in genericParameters) { + if (identifier.equals(genericParameter.identifier, ignoreCase = true)) { + param = genericParameter + break + } + } + if (!inCurrentClassOnly && param == null && baseType is ClassType) { + param = (baseType as ClassType).getGenericParameterByIdentifier(identifier) + } + return param + } + + val sortedElements: List + get() = elements.sortedWith { o1, o2 -> o1.name.compareTo(o2.name) } + + private var baseElementMap: LinkedHashMap? = null + get() { + if (field == null) { + field = LinkedHashMap() + if (baseType is ClassType) { + (baseType as ClassType).gatherElements(field!!) + } + } + + return field + } + + private fun gatherElements(elementMap: LinkedHashMap) { + if (baseType is ClassType) { + (baseType as ClassType).gatherElements(elementMap) + } + + for (element in elements) { + elementMap[element.name] = element + } + } + + val allElements: List + get() { + // Get the baseClass elements into a map by name + val elementMap = LinkedHashMap(baseElementMap) + + // Add this class's elements, overwriting baseClass definitions where applicable + for (el in elements) { + elementMap[el.name] = el + } + + return elementMap.values.toList() + } + + private fun internalAddElement(element: ClassTypeElement) { + val existingElement = baseElementMap!![element.name] + if ( + existingElement != null && + (existingElement.type !is TypeParameter) && + (!(element.type.isSubTypeOf(existingElement.type) || + (existingElement.type is ListType && + element.type.isSubTypeOf(existingElement.type.elementType)) || + (existingElement.type is IntervalType && + element.type.isSubTypeOf(existingElement.type.pointType)) || + (existingElement.type is ChoiceType && + element.type.isCompatibleWith(existingElement.type)))) + ) { + throw InvalidRedeclarationException(this, existingElement, element) + } + + elements.add(element) + } + + private fun internalAddParameter(parameter: TypeParameter) { + // TODO Flesh out and retain method only if needed. + + genericParameters.add(parameter) + } + + fun addElement(element: ClassTypeElement) { + internalAddElement(element) + } + + fun addElements(elements: Collection) { + for (element in elements) { + internalAddElement(element) + } + } + + override fun hashCode(): Int { + return name.hashCode() + } + + override fun equals(other: Any?): Boolean { + if (other is ClassType) { + return this.name == other.name + } + + return false + } + + override fun toString(): String = name + + override fun toLabel(): String = label ?: name + + val tupleType: TupleType + get() = buildTupleType() + + private fun addTupleElements( + classType: ClassType, + elements: LinkedHashMap + ) { + // Add base elements first + val baseType = classType.baseType + if (baseType is ClassType) { + addTupleElements(baseType, elements) + } + + for ((name1, type, prohibited) in classType.elements) { + if (!prohibited) { + val tupleElement = TupleTypeElement(name1, type, false) + elements[tupleElement.name] = tupleElement + } + } + } + + private fun buildTupleType(): TupleType { + val tupleElements = LinkedHashMap() + + addTupleElements(this, tupleElements) + + return TupleType(tupleElements.values.toMutableList()) + } + + override fun isCompatibleWith(other: DataType): Boolean { + if (other is TupleType) { + return tupleType == other + // GitHub #115: It's incorrect for a class type to be considered compatible with another + // class type on the + // basis of the inferred tuple type alone. + // } else if (other instanceof ClassType) { + // ClassType classType = (ClassType)other; + // return getTupleType().equals(classType.getTupleType()); + } + + return super.isCompatibleWith(other) + } + + override val isGeneric: Boolean + get() = genericParameters.isNotEmpty() + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + return if (callType is ClassType && callType.elements.size == elements.size) { + sortedElements.zip(callType.sortedElements).all { (thisElement, thatElement) -> + thisElement.type.isInstantiable(thatElement.type, context) + } + } else false + } + + override fun instantiate(context: InstantiationContext): DataType { + if (!isGeneric) { + return this + } + + return ClassType( + name, + baseType, + elements.map { ClassTypeElement(it.name, it.type.instantiate(context)) }.toMutableList() + ) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.java b/Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.java deleted file mode 100644 index 0367b2be8..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.hl7.cql.model; - -public class ClassTypeElement { - private String name; - private DataType type; - private boolean prohibited; - private boolean oneBased; - private String target; - - public ClassTypeElement(String name, DataType type, Boolean prohibited, Boolean oneBased, String target) { - if (name == null || name.equals("")) { - throw new IllegalArgumentException("name"); - } - - if (type == null) { - throw new IllegalArgumentException("type"); - } - - this.name = name; - this.type = type; - this.prohibited = prohibited != null ? prohibited : false; - this.oneBased = oneBased != null ? oneBased : false; - this.target = target; - } - - public ClassTypeElement(String name, DataType type) { - this(name, type, false, false, null); - } - - public String getName() { - return this.name; - } - - public DataType getType() { - return this.type; - } - - public boolean isProhibited() { - return prohibited; - } - - public boolean isOneBased() { - return oneBased; - } - - public String getTarget() { - return target; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ClassTypeElement)) { - return false; - } - - ClassTypeElement that = (ClassTypeElement) o; - - if (target != null && !target.equals(that.target)) { - return false; - } - if (oneBased != that.oneBased) { - return false; - } - if (prohibited != that.prohibited) { - return false; - } - if (!name.equals(that.name)) { - return false; - } - if (!type.equals(that.type)) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + type.hashCode(); - result = 31 * result + (prohibited ? 1 : 0); - result = 31 * result + (oneBased ? 1 : 0); - if (target != null) { - result = 31 * result + (target.hashCode()); - } - return result; - } - - public boolean isSubTypeOf(ClassTypeElement that) { - return this.getName().equals(that.getName()) && this.getType().isSubTypeOf(that.getType()); - } - - public boolean isSuperTypeOf(ClassTypeElement that) { - return this.getName().equals(that.getName()) && this.getType().isSuperTypeOf(that.getType()); - } - - @Override - public String toString() { - return String.format( - "%s:%s%s%s%s", - this.name, - this.type.toString(), - this.prohibited ? " (prohibited)" : "", - this.oneBased ? " (one-based)" : "", - this.target != null ? " (target: " + this.target + ")" : ""); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.kt new file mode 100644 index 000000000..08497263e --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ClassTypeElement.kt @@ -0,0 +1,36 @@ +package org.hl7.cql.model + +import java.util.* + +data class ClassTypeElement( + val name: String, + val type: DataType, + val prohibited: Boolean = false, + val oneBased: Boolean = false, + val target: String? = null +) { + + init { + require(name.isNotEmpty()) { "A class type element must have a name." } + } + + fun isSubTypeOf(that: ClassTypeElement): Boolean { + return this.name == that.name && type.isSubTypeOf(that.type) + } + + fun isSuperTypeOf(that: ClassTypeElement): Boolean { + return this.name == that.name && type.isSuperTypeOf(that.type) + } + + override fun toString(): String { + return String.format( + Locale.US, + "%s:%s%s%s%s", + this.name, + type.toString(), + if (this.prohibited) " (prohibited)" else "", + if (this.oneBased) " (one-based)" else "", + if (this.target != null) " (target: " + this.target + ")" else "" + ) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/DataType.java b/Src/java/model/src/main/java/org/hl7/cql/model/DataType.java deleted file mode 100644 index 9a5841e82..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/DataType.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.hl7.cql.model; - -// One-time exception for this class -// to avoid any backwards-incompatible changes -@SuppressWarnings("checkstyle:abstractclassname") -public abstract class DataType { - public DataType() { - this(null); - } - - public DataType(DataType baseType) { - this.baseType = baseType == null ? DataType.ANY : baseType; - } - - private DataType baseType; - - public DataType getBaseType() { - return baseType; - } - - public String toLabel() { - return toString(); - } - - public boolean isSubTypeOf(DataType other) { - DataType currentType = this; - while (currentType != null) { - if (currentType.equals(other)) { - return true; - } - currentType = currentType.baseType; - } - - return false; - } - - public boolean isSuperTypeOf(DataType other) { - while (other != null) { - if (equals(other)) { - return true; - } - other = other.baseType; - } - - return false; - } - - /** - * @param other - * @return The first supertype of this type that is also a supertype of other - */ - public DataType getCommonSuperTypeOf(DataType other) { - DataType currentType = this; - while (currentType != null) { - if (currentType.isSuperTypeOf(other)) { - return currentType; - } - currentType = currentType.baseType; - } - - return null; - } - - // Note that this is not how implicit/explicit conversions are defined, the notion of - // type compatibility is used to support implicit casting, such as casting a "null" - // literal to any other type, or casting a class to an equivalent tuple. - public boolean isCompatibleWith(DataType other) { - // A type is compatible with a choice type if it is a subtype of one of the choice types - if (other instanceof ChoiceType) { - for (DataType choice : ((ChoiceType) other).getTypes()) { - if (this.isSubTypeOf(choice)) { - return true; - } - } - } - - return this.equals(other); // Any data type is compatible with itself - } - - public abstract boolean isGeneric(); - - public abstract boolean isInstantiable(DataType callType, InstantiationContext context); - - public abstract DataType instantiate(InstantiationContext context); - - public static final SimpleType ANY = new SimpleType("System.Any"); -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/DataType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/DataType.kt new file mode 100644 index 000000000..504c5c329 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/DataType.kt @@ -0,0 +1,28 @@ +package org.hl7.cql.model + +interface DataType { + val baseType: DataType + + fun toLabel(): String + + fun isSubTypeOf(other: DataType): Boolean + + fun isSuperTypeOf(other: DataType): Boolean + + fun getCommonSuperTypeOf(other: DataType): DataType + + // Note that this is not how implicit/explicit conversions are defined, the notion of + // type compatibility is used to support implicit casting, such as casting a "null" + // literal to any other type, or casting a class to an equivalent tuple. + fun isCompatibleWith(other: DataType): Boolean + + val isGeneric: Boolean + + fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean + + fun instantiate(context: InstantiationContext): DataType + + companion object { + @JvmField val ANY: SimpleType = SimpleType("System.Any") + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.java b/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.java deleted file mode 100644 index a743dfd58..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.java +++ /dev/null @@ -1,393 +0,0 @@ -package org.hl7.cql.model; - -import java.util.List; -import java.util.Map; -import org.apache.commons.lang3.StringUtils; - -/** - * The GenericClassSignatureParser is a convenience class for the parsing of generic signature - * and the creation of the corresponding CQL DataTypes, namely, GenericClassType and GenericClassProfileType. - * The former is used to capture the declaration of a GenericClass such as List<T>. The latter is used to capture a new type - * such as 'IntegerList' formed by binding types to generic parameters such as List<Integer>. - */ -public class GenericClassSignatureParser { - - public static final CharSequence OPEN_BRACKET = "<"; - public static final CharSequence CLOSE_BRACKET = ">"; - public static final String EXTENDS = "extends"; - - private int startPos = 0; - private int endPos = 0; - private int bracketCount = 0; - private int currentBracketPosition = 0; - private Map resolvedTypes; - - /** - * A generic signature such as List<T> or a bound signature - * such as List<Person> - */ - private String genericSignature; - - /** - * The base type for the class type or the profile. - */ - private String baseType; - - /** - * The name of a bound type such as PersonList = List<Person> - */ - private String boundGenericTypeName; - - public GenericClassSignatureParser( - String genericSignature, - String baseType, - String boundGenericTypeName, - Map resolvedTypes) { - this.genericSignature = genericSignature; - this.resolvedTypes = resolvedTypes; - this.baseType = baseType; - this.boundGenericTypeName = boundGenericTypeName; - } - - public GenericClassSignatureParser(String genericSignature, Map resolvedTypes) { - this(genericSignature, null, null, resolvedTypes); - } - - public String getGenericSignature() { - return genericSignature; - } - - public void setGenericSignature(String genericSignature) { - this.genericSignature = genericSignature; - } - - public String getBaseType() { - return baseType; - } - - public void setBaseType(String baseType) { - this.baseType = baseType; - } - - public String getBoundGenericTypeName() { - return boundGenericTypeName; - } - - public void setBoundGenericTypeName(String boundGenericTypeName) { - this.boundGenericTypeName = boundGenericTypeName; - } - - /** - * Parses a generic type declaration such as Map<K,V>. - * - * @return Generic class constructed from this definition. - */ - public ClassType parseGenericSignature() { - String genericTypeName = genericSignature; - String[] params = new String[0]; - if (isValidGenericSignature()) { - genericTypeName = genericSignature.substring(0, genericSignature.indexOf('<')); - String parameters = - genericSignature.substring(genericSignature.indexOf('<') + 1, genericSignature.lastIndexOf('>')); - params = escapeNestedCommas(parameters).split(","); - } - String baseTypeName = baseType; - String[] baseTypeParameters = null; - if (baseType != null && baseType.contains("<")) { - baseTypeName = baseType.substring(0, baseType.indexOf('<')); - String baseTypeParameterString = baseType.substring(baseType.indexOf('<') + 1, baseType.lastIndexOf('>')); - baseTypeParameters = escapeNestedCommas(baseTypeParameterString).split(","); - } - DataType baseDataType = resolveTypeName(baseTypeName); - ClassType genericClassType = new ClassType(genericTypeName, baseDataType); - for (String param : params) { - TypeParameter paramType = handleParameterDeclaration(unescapeNestedCommas(param)); - genericClassType.addGenericParameter(paramType); - } - if (baseTypeParameters != null) { - int index = 0; - for (String baseTypeParameter : baseTypeParameters) { - if (baseTypeParameter.length() == 1 - && genericClassType.getGenericParameterByIdentifier(baseTypeParameter) == null) { - throw new RuntimeException("Cannot resolve symbol " + baseTypeParameter); - } else { - DataType boundType = resolveTypeName(unescapeNestedCommas(baseTypeParameter)); - ClassType baseTypeClass = (ClassType) baseDataType; - List baseClassFields = baseTypeClass.getElements(); - String myParam = - baseTypeClass.getGenericParameters().get(index).getIdentifier(); - System.out.println(boundType + " replaces param " + myParam); - for (ClassTypeElement baseClassField : baseClassFields) { - ClassTypeElement myElement = new ClassTypeElement(baseClassField.getName(), boundType); - genericClassType.addElement(myElement); - } - } - index++; - } - } - return genericClassType; - } - - /** - * Method handles a generic parameter declaration such as T, or T extends MyType. - * - * @param parameterString - * @return Type parameter for this parameter for this string declaration. - */ - protected TypeParameter handleParameterDeclaration(String parameterString) { - String[] paramComponents = parameterString.split("\\s+"); - if (paramComponents.length == 1) { - return new TypeParameter( - StringUtils.trim(parameterString), TypeParameter.TypeParameterConstraint.NONE, null); - } else if (paramComponents.length == 3) { - if (paramComponents[1].equalsIgnoreCase(EXTENDS)) { - return new TypeParameter( - paramComponents[0], - TypeParameter.TypeParameterConstraint.TYPE, - resolveTypeName(paramComponents[2])); - } else { - throw new RuntimeException("Invalid parameter syntax: " + parameterString); - } - } else { - throw new RuntimeException("Invalid parameter syntax: " + parameterString); - } - } - - /** - * Identifies the data type for the named type. If the argument is null, - * the return type will be null. - * - * @param parameterType - * @return The parameter's type - */ - protected DataType resolveTypeName(String parameterType) { - if (isValidGenericSignature(parameterType)) { - return handleBoundType(parameterType); - } else { - if (parameterType == null) { - return null; - } else { - return resolveType(parameterType); - } - } - } - - /** - * Method resolves bound type if it exists or else creates it and - * adds it to the resolved type index. - * - * @param boundGenericSignature - * @return The bound type created or resolved - */ - protected DataType handleBoundType(String boundGenericSignature) { - ClassType resolvedType = (ClassType) resolvedTypes.get(escapeNestedAngleBrackets(boundGenericSignature)); - if (resolvedType != null) { - return resolvedType; - } else { - String genericTypeName = boundGenericSignature.substring(0, boundGenericSignature.indexOf('<')); - resolvedType = (ClassType) resolveType(genericTypeName); - if (resolvedType == null) { - throw new RuntimeException("Unknown type " + genericTypeName); - } - ClassType newType = new ClassType(escapeNestedAngleBrackets(boundGenericSignature), resolvedType); - String parameters = boundGenericSignature.substring( - boundGenericSignature.indexOf('<') + 1, boundGenericSignature.lastIndexOf('>')); - String[] params = escapeNestedCommas(parameters).split(","); - int index = 0; - for (String param : params) { - DataType boundParam = null; - param = unescapeNestedCommas(param); - if (isValidGenericSignature(param)) { - boundParam = handleBoundType(param); - } else { - boundParam = resolveType(param); - } - TypeParameter typeParameter = - resolvedType.getGenericParameters().get(index); - for (ClassTypeElement classTypeElement : resolvedType.getElements()) { - if (classTypeElement.getType() instanceof TypeParameter) { - if (((TypeParameter) classTypeElement.getType()) - .getIdentifier() - .equalsIgnoreCase(typeParameter.getIdentifier())) { - ClassTypeElement newElement = new ClassTypeElement(classTypeElement.getName(), boundParam); - newType.addElement(newElement); - } - } - } - index++; - } - resolvedTypes.put(newType.getName(), newType); - return newType; - } - } - - /** - * Returns true if the generic signature assigned to this object is well-formed. - * - * @return True if the generic signature is valid - */ - public boolean isValidGenericSignature() { - return isValidGenericSignature(genericSignature); - } - - /** - * Returns true if the generic signature passed as an argument is well-formed. - * - * @param genericSignature - * @return True if the generic signature is valid - */ - public boolean isValidGenericSignature(String genericSignature) { - return areBracketsPaired(genericSignature) && closingBracketsComeAfterOpeningBrackets(genericSignature); - } - - /** - * Method sets the initial state of this parser. - */ - private void initializeParser() { - startPos = genericSignature.indexOf('<'); - endPos = genericSignature.lastIndexOf('>'); - bracketCount = openBracketCount(); - } - - /** - * Counts the number of < in this signature. - * - * @return - */ - private int openBracketCount() { - return openBracketCount(genericSignature); - } - - /** - * Counts the number of < in this signature. - * - * @param signatureString - * @return - */ - private int openBracketCount(String signatureString) { - int matchCount = 0; - if (signatureString != null) { - matchCount = StringUtils.countMatches(signatureString, OPEN_BRACKET); - } - return matchCount; - } - - /** - * Counts the number of > in this signature. - * - * @return - */ - private int closeBracketCount() { - return closeBracketCount(genericSignature); - } - - /** - * Counts the number of > in this signature. - * - * @param signatureString - * @return - */ - private int closeBracketCount(String signatureString) { - int matchCount = 0; - if (signatureString != null) { - matchCount = StringUtils.countMatches(signatureString, CLOSE_BRACKET); - } - return matchCount; - } - - /** - * Method returns if the number of < matches the number of > - * - * @return - */ - private boolean areBracketsPaired() { - return areBracketsPaired(genericSignature); - } - - /** - * Method returns if the number of < matches the number of > - * - * @param signatureString - * @return - */ - private boolean areBracketsPaired(String signatureString) { - boolean paired = false; - if (signatureString != null) { - int openCount = openBracketCount(signatureString); - int closeCount = closeBracketCount(signatureString); - paired = (openCount == closeCount) && (openCount > 0); - } - return paired; - } - - /** - * Simple check to make sure that closing brackets come after opening brackets. - * - * @return - */ - private boolean closingBracketsComeAfterOpeningBrackets() { - return closingBracketsComeAfterOpeningBrackets(genericSignature); - } - - /** - * Simple check to make sure that closing brackets come after opening brackets. - * - * @param signatureString - * @return - */ - private boolean closingBracketsComeAfterOpeningBrackets(String signatureString) { - return signatureString != null && signatureString.lastIndexOf('<') < signatureString.indexOf('>'); - } - - /** - * Convenience method for the parsing of nested comma-separated parameters. - * Call before unescapeNestedCommas when done processing the top level of nested parameters. - * - * @param signature - * @return - */ - private String escapeNestedCommas(String signature) { - char[] signatureCharArray = signature.toCharArray(); - int openBracketCount = 0; - for (int index = 0; index < signatureCharArray.length; index++) { - char c = signatureCharArray[index]; - if (c == '<') { - openBracketCount++; - } else if (c == '>') { - openBracketCount--; - } else if (c == ',' && openBracketCount > 0) { - signatureCharArray[index] = '|'; - } - } - return new String(signatureCharArray); - } - - /** - * Convenience method for the parsing of nested comma-separated parameters. - * Call after escapeNestedCommas when handling the top level of nested parameters. - * - * @param escapedSignature - * @return - */ - private String unescapeNestedCommas(String escapedSignature) { - return escapedSignature.replaceAll("\\|", ","); - } - - /** - * Method looks up data type of typeName is index. - * - * @param typeName - * @return - */ - private DataType resolveType(String typeName) { - DataType type = resolvedTypes.get(typeName); - if (type == null) { - throw new RuntimeException("Unable to resolve " + typeName); - } - return type; - } - - private String escapeNestedAngleBrackets(String genericSignature) { - return genericSignature.replaceAll("<", "[").replaceAll(">", "]"); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.kt b/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.kt new file mode 100644 index 000000000..f3d83b34c --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.kt @@ -0,0 +1,305 @@ +package org.hl7.cql.model + +/** + * The GenericClassSignatureParser is a convenience class for the parsing of generic signature and + * the creation of the corresponding CQL DataTypes, namely, GenericClassType and + * GenericClassProfileType. The former is used to capture the declaration of a GenericClass such as + * List<T>. The latter is used to capture a new type such as 'IntegerList' formed by binding + * types to generic parameters such as List<Integer>. + */ +@Suppress("MagicNumber", "TooGenericExceptionThrown", "TooManyFunctions", "NestedBlockDepth") +class GenericClassSignatureParser( + /** A generic signature such as List<T> or a bound signature such as List<Person> */ + private var genericSignature: String, + /** The base type for the class type or the profile. */ + var baseType: String?, + private val resolvedTypes: MutableMap +) { + @JvmOverloads + constructor( + genericSignature: String, + resolvedTypes: MutableMap = HashMap() + ) : this(genericSignature, null, resolvedTypes) + + /** + * Parses a generic type declaration such as Map<K,V>. + * + * @return Generic class constructed from this definition. + */ + fun parseGenericSignature(): ClassType { + var genericTypeName = genericSignature + var params = listOf() + if (isValidGenericSignature) { + genericTypeName = genericSignature.substring(0, genericSignature.indexOf('<')) + val parameters = + genericSignature.substring( + genericSignature.indexOf('<') + 1, + genericSignature.lastIndexOf('>') + ) + params = + escapeNestedCommas(parameters).split(",".toRegex()).dropLastWhile { it.isEmpty() } + } + var baseTypeName = baseType + var baseTypeParameters: List? = null + if (baseType != null && baseType!!.contains("<")) { + baseTypeName = baseType!!.substring(0, baseType!!.indexOf('<')) + val baseTypeParameterString = + baseType!!.substring(baseType!!.indexOf('<') + 1, baseType!!.lastIndexOf('>')) + baseTypeParameters = + escapeNestedCommas(baseTypeParameterString).split(",".toRegex()).dropLastWhile { + it.isEmpty() + } + } + val baseDataType = resolveTypeName(baseTypeName) + val genericClassType = ClassType(genericTypeName, baseDataType) + for (param in params) { + val paramType = handleParameterDeclaration(unescapeNestedCommas(param)) + genericClassType.addGenericParameter(paramType) + } + if (baseTypeParameters != null) { + for (baseTypeParameter in baseTypeParameters) { + if ( + baseTypeParameter.length == 1 && + genericClassType.getGenericParameterByIdentifier(baseTypeParameter) == null + ) { + throw RuntimeException("Cannot resolve symbol $baseTypeParameter") + } else { + val boundType = resolveTypeName(unescapeNestedCommas(baseTypeParameter)) + val baseTypeClass = baseDataType as ClassType? + val baseClassFields: List = baseTypeClass!!.elements + for ((name) in baseClassFields) { + val myElement = + ClassTypeElement( + name, + boundType!!, + prohibited = false, + oneBased = false, + target = null + ) + genericClassType.addElement(myElement) + } + } + } + } + return genericClassType + } + + /** + * Method handles a generic parameter declaration such as T, or T extends MyType. + * + * @param parameterString + * @return Type parameter for this parameter for this string declaration. + */ + private fun handleParameterDeclaration(parameterString: String): TypeParameter { + val paramComponents = parameterString.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() } + return if (paramComponents.size == 1) { + TypeParameter(parameterString.trim(), TypeParameter.TypeParameterConstraint.NONE, null) + } else if (paramComponents.size == 3) { + if (paramComponents[1].equals(EXTENDS, ignoreCase = true)) { + TypeParameter( + paramComponents[0], + TypeParameter.TypeParameterConstraint.TYPE, + resolveTypeName(paramComponents[2]) + ) + } else { + throw RuntimeException("Invalid parameter syntax: $parameterString") + } + } else { + throw RuntimeException("Invalid parameter syntax: $parameterString") + } + } + + /** + * Identifies the data type for the named type. If the argument is null, the return type will be + * null. + * + * @param parameterType + * @return The parameter's type + */ + private fun resolveTypeName(parameterType: String?): DataType? { + return if (isValidGenericSignature(parameterType)) { + handleBoundType(parameterType!!) + } else { + if (parameterType == null) { + null + } else { + resolveType(parameterType) + } + } + } + + /** + * Method resolves bound type if it exists or else creates it and adds it to the resolved type + * index. + * + * @param boundGenericSignature + * @return The bound type created or resolved + */ + private fun handleBoundType(boundGenericSignature: String): DataType { + var resolvedType = + resolvedTypes[escapeNestedAngleBrackets(boundGenericSignature)] as? ClassType + if (resolvedType != null) { + return resolvedType + } else { + val genericTypeName = + boundGenericSignature.substring(0, boundGenericSignature.indexOf('<')) + resolvedType = resolveType(genericTypeName) as ClassType + val newType = ClassType(escapeNestedAngleBrackets(boundGenericSignature), resolvedType) + val parameters = + boundGenericSignature.substring( + boundGenericSignature.indexOf('<') + 1, + boundGenericSignature.lastIndexOf('>') + ) + val params = + escapeNestedCommas(parameters).split(",".toRegex()).dropLastWhile { it.isEmpty() } + for ((index, param) in params.withIndex()) { + var boundParam: DataType? + val unescaped = unescapeNestedCommas(param) + boundParam = + if (isValidGenericSignature(unescaped)) { + handleBoundType(unescaped) + } else { + resolveType(unescaped) + } + val typeParameter = resolvedType.genericParameters[index] + for ((name, type) in resolvedType.elements) { + if ( + type is TypeParameter && + type.identifier.equals(typeParameter.identifier, ignoreCase = true) + ) { + val newElement = + ClassTypeElement( + name, + boundParam, + prohibited = false, + oneBased = false, + target = null + ) + newType.addElement(newElement) + } + } + } + resolvedTypes[newType.name] = newType + return newType + } + } + + val isValidGenericSignature: Boolean + /** + * Returns true if the generic signature assigned to this object is well-formed. + * + * @return True if the generic signature is valid + */ + get() = isValidGenericSignature(genericSignature) + + /** + * Returns true if the generic signature passed as an argument is well-formed. + * + * @param genericSignature + * @return True if the generic signature is valid + */ + private fun isValidGenericSignature(genericSignature: String?): Boolean { + return areBracketsPaired(genericSignature) && + closingBracketsComeAfterOpeningBrackets(genericSignature) + } + + /** + * Counts the number of < in this signature. + * + * @return + */ + private fun openBracketCount(signatureString: String? = genericSignature): Int { + return signatureString?.count { it == OPEN_BRACKET } ?: 0 + } + + /** + * Counts the number of > in this signature. + * + * @return + */ + private fun closeBracketCount(signatureString: String? = genericSignature): Int { + return signatureString?.count { it == CLOSE_BRACKET } ?: 0 + } + + /** + * Method returns if the number of < matches the number of > + * + * @return + */ + private fun areBracketsPaired(signatureString: String? = genericSignature): Boolean { + var paired = false + if (signatureString != null) { + val openCount = openBracketCount(signatureString) + val closeCount = closeBracketCount(signatureString) + paired = (openCount == closeCount) && (openCount > 0) + } + return paired + } + + /** + * Simple check to make sure that closing brackets come after opening brackets. + * + * @return + */ + private fun closingBracketsComeAfterOpeningBrackets( + signatureString: String? = genericSignature + ): Boolean { + return signatureString != null && + signatureString.lastIndexOf('<') < signatureString.indexOf('>') + } + + /** + * Convenience method for the parsing of nested comma-separated parameters. Call before + * unescapeNestedCommas when done processing the top level of nested parameters. + * + * @param signature + * @return + */ + private fun escapeNestedCommas(signature: String): String { + val signatureCharArray = signature.toCharArray() + var openBracketCount = 0 + for (index in signatureCharArray.indices) { + val c = signatureCharArray[index] + if (c == '<') { + openBracketCount++ + } else if (c == '>') { + openBracketCount-- + } else if (c == ',' && openBracketCount > 0) { + signatureCharArray[index] = '|' + } + } + return String(signatureCharArray) + } + + /** + * Convenience method for the parsing of nested comma-separated parameters. Call after + * escapeNestedCommas when handling the top level of nested parameters. + * + * @param escapedSignature + * @return + */ + private fun unescapeNestedCommas(escapedSignature: String): String { + return escapedSignature.replace("\\|".toRegex(), ",") + } + + /** + * Method looks up data type of typeName is index. + * + * @param typeName + * @return + */ + private fun resolveType(typeName: String): DataType { + val type = resolvedTypes[typeName] ?: throw RuntimeException("Unable to resolve $typeName") + return type + } + + private fun escapeNestedAngleBrackets(genericSignature: String): String { + return genericSignature.replace("<".toRegex(), "[").replace(">".toRegex(), "]") + } + + companion object { + const val OPEN_BRACKET = '<' + const val CLOSE_BRACKET = '>' + const val EXTENDS: String = "extends" + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.java b/Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.java deleted file mode 100644 index 34689e831..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.hl7.cql.model; - -public interface InstantiationContext { - boolean isInstantiable(TypeParameter parameter, DataType callType); - - DataType instantiate(TypeParameter parameter); - - Iterable getSimpleConversionTargets(DataType callType); - - Iterable getIntervalConversionTargets(DataType callType); - - Iterable getListConversionTargets(DataType callType); -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.kt b/Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.kt new file mode 100644 index 000000000..3a97b7cb4 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/InstantiationContext.kt @@ -0,0 +1,13 @@ +package org.hl7.cql.model + +interface InstantiationContext { + fun isInstantiable(parameter: TypeParameter, callType: DataType): Boolean + + fun instantiate(parameter: TypeParameter): DataType + + fun getSimpleConversionTargets(callType: DataType): List + + fun getIntervalConversionTargets(callType: DataType): List + + fun getListConversionTargets(callType: DataType): List +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.java b/Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.java deleted file mode 100644 index 273859720..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.hl7.cql.model; - -public class IntervalType extends DataType { - private DataType pointType; - - public IntervalType(DataType pointType) { - super(); - - if (pointType == null) { - throw new IllegalArgumentException("pointType"); - } - - this.pointType = pointType; - } - - public DataType getPointType() { - return this.pointType; - } - - @Override - public int hashCode() { - return 53 * pointType.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof IntervalType) { - IntervalType that = (IntervalType) o; - return this.pointType.equals(that.pointType); - } - - return false; - } - - @Override - public boolean isSubTypeOf(DataType other) { - if (other instanceof IntervalType) { - IntervalType that = (IntervalType) other; - return this.pointType.isSubTypeOf(that.pointType); - } - - return super.isSubTypeOf(other); - } - - @Override - public boolean isSuperTypeOf(DataType other) { - if (other instanceof IntervalType) { - IntervalType that = (IntervalType) other; - return this.pointType.isSuperTypeOf(that.pointType); - } - - return super.isSuperTypeOf(other); - } - - @Override - public String toString() { - return String.format("interval<%s>", pointType.toString()); - } - - @Override - public String toLabel() { - return String.format("Interval of %s", pointType.toLabel()); - } - - @Override - public boolean isGeneric() { - return pointType.isGeneric(); - } - - @Override - public boolean isInstantiable(DataType callType, InstantiationContext context) { - if (callType.equals(DataType.ANY)) { - return pointType.isInstantiable(callType, context); - } - - if (callType instanceof IntervalType) { - IntervalType intervalType = (IntervalType) callType; - return pointType.isInstantiable(intervalType.pointType, context); - } - - boolean isAlreadyInstantiable = false; - for (IntervalType targetIntervalType : context.getIntervalConversionTargets(callType)) { - boolean isInstantiable = pointType.isInstantiable(targetIntervalType.pointType, context); - if (isInstantiable) { - if (isAlreadyInstantiable) { - throw new IllegalArgumentException(String.format( - "Ambiguous generic instantiation involving %s to %s.", - callType.toString(), targetIntervalType.toString())); - } - isAlreadyInstantiable = true; - } - } - - if (isAlreadyInstantiable) { - return true; - } - - return false; - } - - @Override - public DataType instantiate(InstantiationContext context) { - return new IntervalType(pointType.instantiate(context)); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.kt new file mode 100644 index 000000000..087170299 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/IntervalType.kt @@ -0,0 +1,60 @@ +package org.hl7.cql.model + +import java.util.* +import org.hl7.cql.model.DataType.Companion.ANY + +data class IntervalType(val pointType: DataType) : BaseDataType() { + override fun isSubTypeOf(other: DataType): Boolean { + return if (other is IntervalType) { + return pointType.isSubTypeOf(other.pointType) + } else { + super.isSubTypeOf(other) + } + } + + override fun isSuperTypeOf(other: DataType): Boolean { + return if (other is IntervalType) { + return pointType.isSuperTypeOf(other.pointType) + } else { + super.isSuperTypeOf(other) + } + } + + override fun toString(): String { + return String.format(Locale.US, "interval<%s>", pointType.toString()) + } + + override fun toLabel(): String { + return String.format(Locale.US, "Interval of %s", pointType.toLabel()) + } + + override val isGeneric: Boolean + get() = pointType.isGeneric + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + return when (callType) { + ANY -> pointType.isInstantiable(callType, context) + is IntervalType -> pointType.isInstantiable(callType.pointType, context) + else -> { + val instantiableElements = + context.getIntervalConversionTargets(callType).filter { + pointType.isInstantiable(it.pointType, context) + } + require(instantiableElements.size <= 1) { + String.format( + Locale.US, + "Ambiguous generic instantiation involving %s to %s.", + callType.toString(), + instantiableElements.toString() + ) + } + + instantiableElements.isNotEmpty() + } + } + } + + override fun instantiate(context: InstantiationContext): DataType { + return IntervalType(pointType.instantiate(context)) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.java b/Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.java deleted file mode 100644 index 45319acb5..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.hl7.cql.model; - -public class InvalidRedeclarationException extends IllegalArgumentException { - public InvalidRedeclarationException() { - super(); - } - - public InvalidRedeclarationException(String s) { - super(s); - } - - public InvalidRedeclarationException(ClassType classType, ClassTypeElement original, ClassTypeElement redeclared) { - super(String.format( - "%s.%s cannot be redeclared with type %s because it is not a subtype of the original element type %s", - classType.getName(), redeclared.getName(), redeclared.getType(), original.getType())); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.kt b/Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.kt new file mode 100644 index 000000000..08e75d8d9 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/InvalidRedeclarationException.kt @@ -0,0 +1,24 @@ +package org.hl7.cql.model + +import java.util.* + +class InvalidRedeclarationException : IllegalArgumentException { + constructor() : super() + + constructor(s: String?) : super(s) + + constructor( + classType: ClassType, + original: ClassTypeElement, + redeclared: ClassTypeElement + ) : super( + String.format( + Locale.US, + "%s.%s cannot be redeclared with type %s because it is not a subtype of the original element type %s", + classType.name, + redeclared.name, + redeclared.type, + original.type + ) + ) +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ListType.java b/Src/java/model/src/main/java/org/hl7/cql/model/ListType.java deleted file mode 100644 index 13dd6b7e3..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ListType.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.hl7.cql.model; - -public class ListType extends DataType { - private DataType elementType; - - public ListType(DataType elementType) { - super(); - - if (elementType == null) { - throw new IllegalArgumentException("elementType"); - } - - this.elementType = elementType; - } - - public DataType getElementType() { - return this.elementType; - } - - @Override - public int hashCode() { - return 67 * elementType.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof ListType) { - ListType that = (ListType) o; - return this.elementType.equals(that.elementType); - } - - return false; - } - - @Override - public boolean isSubTypeOf(DataType other) { - if (other instanceof ListType) { - ListType that = (ListType) other; - return this.elementType.isSubTypeOf(that.elementType); - } - - return super.isSubTypeOf(other); - } - - @Override - public boolean isSuperTypeOf(DataType other) { - if (other instanceof ListType) { - ListType that = (ListType) other; - return this.elementType.isSuperTypeOf(that.elementType); - } - - return super.isSuperTypeOf(other); - } - - @Override - public String toString() { - return String.format("list<%s>", elementType.toString()); - } - - @Override - public String toLabel() { - return String.format("List of %s", elementType.toLabel()); - } - - @Override - public boolean isGeneric() { - return elementType.isGeneric(); - } - - @Override - public boolean isInstantiable(DataType callType, InstantiationContext context) { - if (callType.equals(DataType.ANY)) { - return elementType.isInstantiable(callType, context); - } - - if (callType instanceof ListType) { - ListType listType = (ListType) callType; - return elementType.isInstantiable(listType.elementType, context); - } - - boolean isAlreadyInstantiable = false; - for (ListType targetListType : context.getListConversionTargets(callType)) { - boolean isInstantiable = elementType.isInstantiable(targetListType.elementType, context); - if (isInstantiable) { - if (isAlreadyInstantiable) { - throw new IllegalArgumentException(String.format( - "Ambiguous generic instantiation involving %s to %s.", - callType.toString(), targetListType.toString())); - } - isAlreadyInstantiable = true; - } - } - - if (isAlreadyInstantiable) { - return true; - } - - return false; - } - - @Override - public DataType instantiate(InstantiationContext context) { - return new ListType(elementType.instantiate(context)); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ListType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ListType.kt new file mode 100644 index 000000000..942a01299 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ListType.kt @@ -0,0 +1,59 @@ +package org.hl7.cql.model + +import java.util.* +import org.hl7.cql.model.DataType.Companion.ANY + +data class ListType(val elementType: DataType) : BaseDataType() { + override fun isSubTypeOf(other: DataType): Boolean { + return if (other is ListType) { + elementType.isSubTypeOf(other.elementType) + } else { + super.isSubTypeOf(other) + } + } + + override fun isSuperTypeOf(other: DataType): Boolean { + return if (other is ListType) { + elementType.isSuperTypeOf(other.elementType) + } else { + super.isSuperTypeOf(other) + } + } + + override fun toString(): String { + return String.format(Locale.US, "list<%s>", elementType.toString()) + } + + override fun toLabel(): String { + return String.format(Locale.US, "List of %s", elementType.toLabel()) + } + + override val isGeneric: Boolean = elementType.isGeneric + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + return when (callType) { + ANY -> elementType.isInstantiable(callType, context) + is ListType -> elementType.isInstantiable(callType.elementType, context) + else -> { + val instantiableElements = + context.getListConversionTargets(callType).filter { + elementType.isInstantiable(it.elementType, context) + } + require(instantiableElements.size <= 1) { + String.format( + Locale.US, + "Ambiguous generic instantiation involving %s to %s.", + callType.toString(), + instantiableElements.toString() + ) + } + + instantiableElements.isNotEmpty() + } + } + } + + override fun instantiate(context: InstantiationContext): DataType { + return ListType(elementType.instantiate(context)) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.java b/Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.java deleted file mode 100644 index 89f355f25..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.hl7.cql.model; - -import java.util.ArrayList; - -/** - * Created by Bryn on 3/20/2019. - */ -public class ModelContext { - public ModelContext(String name, ClassType type, Iterable keys, String birthDateElement) { - this.name = name; - this.type = type; - this.birthDateElement = birthDateElement; - if (keys != null) { - for (String key : keys) { - this.keys.add(key); - } - } - } - - private String name; - - public String getName() { - return name; - } - - private ClassType type; - - public ClassType getType() { - return type; - } - - private String birthDateElement; - - public String getBirthDateElement() { - return birthDateElement; - } - - private ArrayList keys = new ArrayList<>(); - - public Iterable getKeys() { - return keys; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.kt new file mode 100644 index 000000000..4971d3c4d --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ModelContext.kt @@ -0,0 +1,9 @@ +package org.hl7.cql.model + +/** Created by Bryn on 3/20/2019. */ +class ModelContext( + val name: String, + val type: ClassType, + val keys: List, + val birthDateElement: String? = null +) diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.java b/Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.java deleted file mode 100644 index 06f701bdf..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.java +++ /dev/null @@ -1,236 +0,0 @@ -package org.hl7.cql.model; - -import jakarta.xml.bind.annotation.*; -import org.jvnet.jaxb2_commons.lang.*; -import org.jvnet.jaxb2_commons.locator.ObjectLocator; -import org.jvnet.jaxb2_commons.locator.util.LocatorUtils; - -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "ModelIdentifier", namespace = "urn:hl7-org:model") -public class ModelIdentifier implements Equals2, HashCode2, ToString2 { - - @XmlAttribute(name = "id") - protected String id; - - @XmlAttribute(name = "system") - protected String system; - - @XmlAttribute(name = "version") - protected String version; - - /** - * Gets the value of the id property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getId() { - return id; - } - - /** - * Sets the value of the id property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setId(String value) { - this.id = value; - } - - /** - * Gets the value of the system property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getSystem() { - return system; - } - - /** - * Sets the value of the system property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setSystem(String value) { - this.system = value; - } - - /** - * Gets the value of the version property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getVersion() { - return version; - } - - /** - * Sets the value of the version property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setVersion(String value) { - this.version = value; - } - - public ModelIdentifier withId(String value) { - setId(value); - return this; - } - - public ModelIdentifier withSystem(String value) { - setSystem(value); - return this; - } - - public ModelIdentifier withVersion(String value) { - setVersion(value); - return this; - } - - public boolean equals( - ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) { - if ((object == null) || (this.getClass() != object.getClass())) { - return false; - } - if (this == object) { - return true; - } - final ModelIdentifier that = ((ModelIdentifier) object); - { - String lhsId; - lhsId = this.getId(); - String rhsId; - rhsId = that.getId(); - if (!strategy.equals( - LocatorUtils.property(thisLocator, "id", lhsId), - LocatorUtils.property(thatLocator, "id", rhsId), - lhsId, - rhsId, - (this.id != null), - (that.id != null))) { - return false; - } - } - { - String lhsSystem; - lhsSystem = this.getSystem(); - String rhsSystem; - rhsSystem = that.getSystem(); - if (!strategy.equals( - LocatorUtils.property(thisLocator, "system", lhsSystem), - LocatorUtils.property(thatLocator, "system", rhsSystem), - lhsSystem, - rhsSystem, - (this.system != null), - (that.system != null))) { - return false; - } - } - { - String lhsVersion; - lhsVersion = this.getVersion(); - String rhsVersion; - rhsVersion = that.getVersion(); - if (!strategy.equals( - LocatorUtils.property(thisLocator, "version", lhsVersion), - LocatorUtils.property(thatLocator, "version", rhsVersion), - lhsVersion, - rhsVersion, - (this.version != null), - (that.version != null))) { - return false; - } - } - return true; - } - - public boolean equals(Object object) { - final EqualsStrategy2 strategy = JAXBEqualsStrategy.getInstance(); - return equals(null, null, object, strategy); - } - - public int hashCode(ObjectLocator locator, HashCodeStrategy2 strategy) { - int currentHashCode = 1; - { - String theId; - theId = this.getId(); - currentHashCode = strategy.hashCode( - LocatorUtils.property(locator, "id", theId), currentHashCode, theId, (this.id != null)); - } - { - String theSystem; - theSystem = this.getSystem(); - currentHashCode = strategy.hashCode( - LocatorUtils.property(locator, "system", theSystem), - currentHashCode, - theSystem, - (this.system != null)); - } - { - String theVersion; - theVersion = this.getVersion(); - currentHashCode = strategy.hashCode( - LocatorUtils.property(locator, "version", theVersion), - currentHashCode, - theVersion, - (this.version != null)); - } - return currentHashCode; - } - - public int hashCode() { - final HashCodeStrategy2 strategy = JAXBHashCodeStrategy.getInstance(); - return this.hashCode(null, strategy); - } - - public String toString() { - final ToStringStrategy2 strategy = JAXBToStringStrategy.getInstance(); - final StringBuilder buffer = new StringBuilder(); - append(null, buffer, strategy); - return buffer.toString(); - } - - public StringBuilder append(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) { - strategy.appendStart(locator, this, buffer); - appendFields(locator, buffer, strategy); - strategy.appendEnd(locator, this, buffer); - return buffer; - } - - public StringBuilder appendFields(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) { - { - String theId; - theId = this.getId(); - strategy.appendField(locator, this, "id", buffer, theId, (this.id != null)); - } - { - String theSystem; - theSystem = this.getSystem(); - strategy.appendField(locator, this, "system", buffer, theSystem, (this.system != null)); - } - { - String theVersion; - theVersion = this.getVersion(); - strategy.appendField(locator, this, "version", buffer, theVersion, (this.version != null)); - } - return buffer; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.kt new file mode 100644 index 000000000..e70d4d3cc --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.kt @@ -0,0 +1,18 @@ +package org.hl7.cql.model + +import jakarta.xml.bind.annotation.XmlAccessType +import jakarta.xml.bind.annotation.XmlAccessorType +import jakarta.xml.bind.annotation.XmlAttribute +import jakarta.xml.bind.annotation.XmlType + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "ModelIdentifier", namespace = "urn:hl7-org:model") +data class ModelIdentifier( + @XmlAttribute(name = "id") var id: String, + @XmlAttribute(name = "system") var system: String? = null, + @XmlAttribute(name = "version") var version: String? = null +) { + init { + require(id.isNotEmpty()) { "id is required" } + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.java b/Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.java deleted file mode 100644 index 74b8a6d75..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.hl7.cql.model; - -import org.hl7.elm_modelinfo.r1.ModelInfo; - -public interface ModelInfoProvider { - ModelInfo load(ModelIdentifier modelIdentifier); -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.kt new file mode 100644 index 000000000..107ca9856 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ModelInfoProvider.kt @@ -0,0 +1,7 @@ +package org.hl7.cql.model + +import org.hl7.elm_modelinfo.r1.ModelInfo + +interface ModelInfoProvider { + fun load(modelIdentifier: ModelIdentifier): ModelInfo? +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamedType.java b/Src/java/model/src/main/java/org/hl7/cql/model/NamedType.java deleted file mode 100644 index 8fd5bf010..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/NamedType.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.hl7.cql.model; - -public interface NamedType { - String getName(); - - String getNamespace(); - - String getSimpleName(); - - String getTarget(); -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamedType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/NamedType.kt new file mode 100644 index 000000000..7ab477073 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/NamedType.kt @@ -0,0 +1,8 @@ +package org.hl7.cql.model + +interface NamedType { + val name: String + val namespace: String + val simpleName: String + var target: String? +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.java b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.java deleted file mode 100644 index d0866bbe3..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.hl7.cql.model; - -public interface NamespaceAware { - void setNamespaceManager(NamespaceManager namespaceManager); -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.kt b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.kt new file mode 100644 index 000000000..f86bfe6b6 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceAware.kt @@ -0,0 +1,5 @@ +package org.hl7.cql.model + +interface NamespaceAware { + fun setNamespaceManager(namespaceManager: NamespaceManager) +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.java b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.java deleted file mode 100644 index 7823df5c2..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.hl7.cql.model; - -public class NamespaceInfo { - public NamespaceInfo(String name, String uri) { - if (name == null || name.isEmpty()) { - throw new IllegalArgumentException("Name is required"); - } - - if (uri == null || uri.isEmpty()) { - throw new IllegalArgumentException("Uri is required"); - } - - this.name = name; - this.uri = uri; - } - - private String name; - - public String getName() { - return name; - } - - private String uri; - - public String getUri() { - return uri; - } - - @Override - public int hashCode() { - return 17 * name.hashCode() ^ 39 * uri.hashCode(); - } - - @Override - public boolean equals(Object that) { - if (that instanceof NamespaceInfo) { - NamespaceInfo thatInfo = (NamespaceInfo) that; - return this.name.equals(thatInfo.getName()) && this.uri.equals(thatInfo.getUri()); - } - - return false; - } - - @Override - public String toString() { - return String.format("%s: %s", name, uri); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.kt b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.kt new file mode 100644 index 000000000..de5694bbd --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.kt @@ -0,0 +1,13 @@ +package org.hl7.cql.model + +import java.util.* + +data class NamespaceInfo(val name: String, val uri: String) { + init { + require(name.isNotEmpty() and uri.isNotEmpty()) { "name and uri are required" } + } + + override fun toString(): String { + return String.format(Locale.US, "%s: %s", name, uri) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.java b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.java deleted file mode 100644 index 951e5329f..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.hl7.cql.model; - -import java.util.HashMap; -import java.util.Map; - -public class NamespaceManager { - private final Map namespaces; - private final Map reverseNamespaces; - - public NamespaceManager() { - namespaces = new HashMap<>(); - reverseNamespaces = new HashMap<>(); - } - - public boolean hasNamespaces() { - return namespaces.size() > 0; - } - - public void ensureNamespaceRegistered(NamespaceInfo namespaceInfo) { - if (namespaceInfo == null) { - throw new IllegalArgumentException("namespaceInfo is required"); - } - - if (!namespaces.containsKey(namespaceInfo.getName())) { - addNamespace(namespaceInfo.getName(), namespaceInfo.getUri()); - } - } - - public void addNamespace(NamespaceInfo namespaceInfo) { - if (namespaceInfo == null) { - throw new IllegalArgumentException("namespaceInfo is required"); - } - - addNamespace(namespaceInfo.getName(), namespaceInfo.getUri()); - } - - public void addNamespace(String namespaceName, String namespaceUri) { - if (namespaceName == null || namespaceName.isEmpty()) { - throw new IllegalArgumentException("namespaceName is required"); - } - - if (namespaceUri == null || namespaceUri.isEmpty()) { - throw new IllegalArgumentException("namespaceUri is required"); - } - - if (namespaces.containsKey(namespaceName)) { - throw new IllegalArgumentException( - String.format("A namespace named %s is already defined.", namespaceName)); - } - - if (reverseNamespaces.containsKey(namespaceUri)) { - throw new IllegalArgumentException( - String.format("A namespace name for uri %s is already defined.", namespaceUri)); - } - - namespaces.put(namespaceName, namespaceUri); - reverseNamespaces.put(namespaceUri, namespaceName); - } - - public String resolveNamespaceUri(String namespaceName) { - if (namespaces.containsKey(namespaceName)) { - return namespaces.get(namespaceName); - } - - return null; - } - - public NamespaceInfo getNamespaceInfoFromUri(String namespaceUri) { - if (reverseNamespaces.containsKey(namespaceUri)) { - return new NamespaceInfo(reverseNamespaces.get(namespaceUri), namespaceUri); - } - - return null; - } - - public static String getPath(String namespaceUri, String name) { - if (namespaceUri != null) { - return String.format("%s/%s", namespaceUri, name); - } - - return name; - } - - public static String getUriPart(String namespaceQualifiedName) { - if (namespaceQualifiedName == null) { - return null; - } - - int i = namespaceQualifiedName.lastIndexOf('/'); - if (i > 0) { - return namespaceQualifiedName.substring(0, i); - } - - return null; - } - - public static String getNamePart(String namespaceQualifiedName) { - if (namespaceQualifiedName == null) { - return null; - } - - int i = namespaceQualifiedName.lastIndexOf("/"); - if (i > 0) { - return namespaceQualifiedName.substring(i + 1); - } - - return namespaceQualifiedName; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.kt b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.kt new file mode 100644 index 000000000..9d3a9faf3 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceManager.kt @@ -0,0 +1,74 @@ +package org.hl7.cql.model + +import java.util.* +import kotlin.collections.HashMap + +class NamespaceManager { + private val namespaces: MutableMap = HashMap() + private val reverseNamespaces: MutableMap = HashMap() + + fun hasNamespaces(): Boolean { + return namespaces.isNotEmpty() + } + + fun ensureNamespaceRegistered(namespaceInfo: NamespaceInfo) { + if (!namespaces.containsKey(namespaceInfo.name)) { + addNamespace(namespaceInfo.name, namespaceInfo.uri) + } + } + + fun addNamespace(namespaceInfo: NamespaceInfo) { + addNamespace(namespaceInfo.name, namespaceInfo.uri) + } + + fun addNamespace(namespaceName: String, namespaceUri: String) { + require(namespaceName.isNotEmpty()) { "namespaceName is required" } + require(namespaceUri.isNotEmpty()) { "namespaceUri is required" } + check(!namespaces.containsKey(namespaceName)) { + String.format(Locale.US, "A namespace named %s is already defined.", namespaceName) + } + + check(!reverseNamespaces.containsKey(namespaceUri)) { + String.format( + Locale.US, + "A namespace name for uri %s is already defined.", + namespaceUri + ) + } + + namespaces[namespaceName] = namespaceUri + reverseNamespaces[namespaceUri] = namespaceName + } + + fun resolveNamespaceUri(namespaceName: String): String? { + return namespaces[namespaceName] + } + + fun getNamespaceInfoFromUri(namespaceUri: String): NamespaceInfo? { + return reverseNamespaces[namespaceUri]?.let { NamespaceInfo(it, namespaceUri) } + } + + companion object { + @JvmStatic + fun getPath(namespaceUri: String?, name: String): String { + return namespaceUri?.let { String.format(Locale.US, "%s/%s", namespaceUri, name) } + ?: name + } + + @JvmStatic + fun getUriPart(namespaceQualifiedName: String?): String? { + return namespaceQualifiedName + ?.lastIndexOf('/') + ?.takeIf { it > 0 } + ?.let { namespaceQualifiedName.substring(0, it) } + } + + @JvmStatic + fun getNamePart(namespaceQualifiedName: String?): String? { + return namespaceQualifiedName + ?.lastIndexOf("/") + ?.takeIf { it >= 0 } + ?.let { namespaceQualifiedName.substring(it + 1) } ?: namespaceQualifiedName + } + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.java b/Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.java deleted file mode 100644 index 10e1e61ff..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.hl7.cql.model; - -import java.util.Collection; - -/** - * Created by Bryn on 8/22/2016. - * - * Profiles within CQL allow the same underlying type to be accessed using different labels. When - * a profile is referenced, the ELM retrieve that is output will use the baseType of the profile as the - * dataType, and the name of the profile as the identifier. This allows the implementation to detect - * when the same data type has been accessed in a different way by the author. - * - * This mechanism is used in QDM to enable the negation pattern. For example, the Encounter, Performed - * data type is the same whether it is accessed positively or negatively, but the difference needs to be - * communicated reliably through the data access layer, so profiles are used, one positive and one negative. - * The underlying type Encounter, Performed, is not retrievable, only the positive and negative profiles. The - * identifiers are set to Encounter, Performed, and Encounter, Not Performed, and the resulting retrieve will - * reflect the EncounterPerformed type, together with the name of the profile, PositiveEncounterPerformed or - * NegativeEncounterPerformed, depending on which profile was used in the retrieve. - * - * NOTE: This behavior was subsequently changed due to the inconsistency it introduces between retrieves - * and general-purpose expressions. QDM still defines a base type with profiles for positive and negative - * aspects, but the retrieve will now return the profile type, not the base type. See github issue #131 for - * a detailed discussion of this change. - */ -public class ProfileType extends ClassType { - public ProfileType(String name, DataType baseType, Collection elements) { - super(name, baseType, elements); - } - - public ProfileType() { - super(); - } - - public ProfileType(String name) { - super(name); - } - - public ProfileType(String name, DataType baseType) { - super(name, baseType); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.kt new file mode 100644 index 000000000..c77641111 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.kt @@ -0,0 +1,31 @@ +package org.hl7.cql.model + +/** + * Created by Bryn on 8/22/2016. + * + * Profiles within CQL allow the same underlying type to be accessed using different labels. When a + * profile is referenced, the ELM retrieve that is output will use the baseType of the profile as + * the dataType, and the name of the profile as the identifier. This allows the implementation to + * detect when the same data type has been accessed in a different way by the author. + * + * This mechanism is used in QDM to enable the negation pattern. For example, the Encounter, + * Performed data type is the same whether it is accessed positively or negatively, but the + * difference needs to be communicated reliably through the data access layer, so profiles are used, + * one positive and one negative. The underlying type Encounter, Performed, is not retrievable, only + * the positive and negative profiles. The identifiers are set to Encounter, Performed, and + * Encounter, Not Performed, and the resulting retrieve will reflect the EncounterPerformed type, + * together with the name of the profile, PositiveEncounterPerformed or NegativeEncounterPerformed, + * depending on which profile was used in the retrieve. + * + * NOTE: This behavior was subsequently changed due to the inconsistency it introduces between + * retrieves and general-purpose expressions. QDM still defines a base type with profiles for + * positive and negative aspects, but the retrieve will now return the profile type, not the base + * type. See github issue #131 for a detailed discussion of this change. + */ +class ProfileType +@JvmOverloads +constructor( + name: String, + baseType: DataType?, + elements: MutableList = mutableListOf() +) : ClassType(name, baseType, elements) diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/Relationship.java b/Src/java/model/src/main/java/org/hl7/cql/model/Relationship.java deleted file mode 100644 index 4f51bf01e..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/Relationship.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.hl7.cql.model; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Bryn on 3/20/2019. - */ -public class Relationship { - public Relationship(ModelContext context, List relatedKeys) { - this.context = context; - this.relatedKeys.addAll(relatedKeys); - } - - private final ModelContext context; - - public ModelContext getContext() { - return context; - } - - private final ArrayList relatedKeys = new ArrayList<>(); - - public List getRelatedKeys() { - return relatedKeys; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/Relationship.kt b/Src/java/model/src/main/java/org/hl7/cql/model/Relationship.kt new file mode 100644 index 000000000..a9435a331 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/Relationship.kt @@ -0,0 +1,4 @@ +package org.hl7.cql.model + +/** Created by Bryn on 3/20/2019. */ +data class Relationship(val context: ModelContext, val relatedKeys: List) diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/SearchType.java b/Src/java/model/src/main/java/org/hl7/cql/model/SearchType.java deleted file mode 100644 index 4134bea50..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/SearchType.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.hl7.cql.model; - -public class SearchType { - - public SearchType(String name, String path, DataType type) { - if (name == null || name.isEmpty()) { - throw new IllegalArgumentException("A name is required to construct a Search"); - } - if (path == null || path.isEmpty()) { - throw new IllegalArgumentException("A path is required to construct a Search"); - } - - this.name = name; - this.path = path; - this.type = type; - } - - private String name; - - public String getName() { - return name; - } - - private String path; - - public String getPath() { - return path; - } - - private DataType type; - - public DataType getType() { - return type; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/SearchType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/SearchType.kt new file mode 100644 index 000000000..37bdb0ec7 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/SearchType.kt @@ -0,0 +1,8 @@ +package org.hl7.cql.model + +class SearchType(val name: String, val path: String, val type: DataType) { + + init { + require(name.isNotEmpty() && path.isNotEmpty()) { "name and path are required" } + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.java b/Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.java deleted file mode 100644 index a1d487812..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.hl7.cql.model; - -public class SimpleType extends DataType implements NamedType { - private String name; - - public SimpleType(String name, DataType baseType) { - super(baseType); - - if (name == null || name.equals("")) { - throw new IllegalArgumentException("name"); - } - this.name = name; - } - - public SimpleType(String name) { - this(name, null); - } - - public String getNamespace() { - int qualifierIndex = this.name.indexOf('.'); - if (qualifierIndex > 0) { - return this.name.substring(0, qualifierIndex); - } - - return ""; - } - - public String getSimpleName() { - int qualifierIndex = this.name.indexOf('.'); - if (qualifierIndex > 0) { - return this.name.substring(qualifierIndex + 1); - } - - return this.name; - } - - public String getName() { - return this.name; - } - - private String target; - - public String getTarget() { - return target; - } - - public void setTarget(String target) { - this.target = target; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof SimpleType) { - SimpleType that = (SimpleType) o; - return this.name.equals(that.name); - } - - return false; - } - - @Override - public String toString() { - return this.name; - } - - @Override - public boolean isCompatibleWith(DataType other) { - // The system type "Any" can be implicitly cast to any other type. - return this.equals(DataType.ANY) || super.isCompatibleWith(other); - } - - @Override - public boolean isGeneric() { - return false; - } - - @Override - public boolean isInstantiable(DataType callType, InstantiationContext context) { - if (isSuperTypeOf(callType)) { - return true; - } - - boolean isAlreadyInstantiable = false; - for (SimpleType targetSimpleType : context.getSimpleConversionTargets(callType)) { - boolean isInstantiable = true; // If it came back from this call, we can instantiate it... - if (isInstantiable) { - if (isAlreadyInstantiable) { - throw new IllegalArgumentException(String.format( - "Ambiguous generic instantiation involving %s to %s.", - callType.toString(), targetSimpleType.toString())); - } - isAlreadyInstantiable = true; - } - } - - if (isAlreadyInstantiable) { - return true; - } - - return false; - } - - @Override - public DataType instantiate(InstantiationContext context) { - return this; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.kt new file mode 100644 index 000000000..3c675c4ab --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/SimpleType.kt @@ -0,0 +1,64 @@ +package org.hl7.cql.model + +import java.util.* + +data class SimpleType +@JvmOverloads +constructor( + override val name: String, + private val base: DataType? = null, + override var target: String? = null +) : BaseDataType(base), NamedType { + + init { + require(name.isNotEmpty()) { "name can not be empty" } + } + + override val namespace: String + get() { + val qualifierIndex = name.indexOf('.') + return if (qualifierIndex > 0) { + name.substring(0, qualifierIndex) + } else "" + } + + override val simpleName: String + get() { + val qualifierIndex = name.indexOf('.') + return if (qualifierIndex > 0) { + name.substring(qualifierIndex + 1) + } else name + } + + override fun toString(): String = name + + override fun isCompatibleWith(other: DataType): Boolean { + // The system type "Any" can be implicitly cast to any other type. + return this == DataType.ANY || super.isCompatibleWith(other) + } + + override val isGeneric: Boolean = false + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + return when { + this.isSuperTypeOf(callType) -> true + else -> { + val instantiableElements = context.getSimpleConversionTargets(callType) + require(instantiableElements.size <= 1) { + String.format( + Locale.US, + "Ambiguous generic instantiation involving %s to %s.", + callType.toString(), + instantiableElements.toString() + ) + } + + instantiableElements.isNotEmpty() + } + } + } + + override fun instantiate(context: InstantiationContext): DataType { + return this + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.java b/Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.java deleted file mode 100644 index b5c19fcad..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.hl7.cql.model; - -import java.io.IOException; -import org.hl7.elm_modelinfo.r1.ModelInfo; -import org.hl7.elm_modelinfo.r1.serializing.ModelInfoReaderFactory; - -public class SystemModelInfoProvider implements ModelInfoProvider { - private NamespaceManager namespaceManager; - - public void setNamespaceManager(NamespaceManager namespaceManager) { - this.namespaceManager = namespaceManager; - } - - private boolean isSystemModelIdentifier(ModelIdentifier modelIdentifier) { - if (namespaceManager != null && namespaceManager.hasNamespaces()) { - return modelIdentifier.getId().equals("System") - && (modelIdentifier.getSystem() == null - || modelIdentifier.getSystem().equals("urn:hl7-org:elm-types:r1")); - } - - return modelIdentifier.getId().equals("System"); - } - - public ModelInfo load(ModelIdentifier modelIdentifier) { - if (isSystemModelIdentifier(modelIdentifier)) { - try { - return ModelInfoReaderFactory.getReader("application/xml") - .read(SystemModelInfoProvider.class.getResourceAsStream( - "/org/hl7/elm/r1/system-modelinfo.xml")); - } catch (IOException e) { - // Do not throw, allow other providers to resolve - } - } - - return null; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.kt b/Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.kt new file mode 100644 index 000000000..a24c62398 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.kt @@ -0,0 +1,30 @@ +package org.hl7.cql.model + +import org.hl7.elm_modelinfo.r1.ModelInfo +import org.hl7.elm_modelinfo.r1.serializing.ModelInfoReaderFactory.getReader + +class SystemModelInfoProvider : ModelInfoProvider { + private var namespaceManager: NamespaceManager? = null + + fun setNamespaceManager(namespaceManager: NamespaceManager) { + this.namespaceManager = namespaceManager + } + + private fun ModelIdentifier.isSystemModelIdentifier(): Boolean { + if (namespaceManager?.hasNamespaces() == true) { + return this.id == "System" && + (this.system == null || this.system == "urn:hl7-org:elm-types:r1") + } + + return this.id == "System" + } + + override fun load(modelIdentifier: ModelIdentifier): ModelInfo? { + return if (modelIdentifier.isSystemModelIdentifier()) { + val reader = getReader("application/xml") + val stream = + this::class.java.getResourceAsStream("/org/hl7/elm/r1/system-modelinfo.xml") + stream?.use { reader?.read(it) } + } else null + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.java b/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.java deleted file mode 100644 index 473d6c9d2..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.java +++ /dev/null @@ -1,216 +0,0 @@ -package org.hl7.cql.model; - -import java.util.*; - -public class TupleType extends DataType { - private List elements = new ArrayList(); - private List sortedElements = null; - - public TupleType(Collection elements) { - super(); - - if (elements != null) { - this.elements.addAll(elements); - } - } - - public TupleType() { - this(null); - } - - public Iterable getElements() { - return elements; - } - - public void addElement(TupleTypeElement element) { - this.elements.add(element); - sortedElements = null; - } - - public void addElements(Collection elements) { - this.elements.addAll(elements); - sortedElements = null; - } - - private List getSortedElements() { - if (sortedElements == null) { - sortedElements = new ArrayList<>(elements); - Collections.sort(sortedElements, (left, right) -> left.getName().compareTo(right.getName())); - } - - return sortedElements; - } - - @Override - public int hashCode() { - int result = 13; - for (int i = 0; i < elements.size(); i++) { - result += (37 * elements.get(i).hashCode()); - } - - return result; - } - - @Override - public boolean equals(Object o) { - if (o instanceof TupleType) { - TupleType that = (TupleType) o; - - if (this.elements.size() == that.elements.size()) { - List theseElements = this.getSortedElements(); - List thoseElements = that.getSortedElements(); - for (int i = 0; i < theseElements.size(); i++) { - if (!theseElements.get(i).equals(thoseElements.get(i))) { - return false; - } - } - - return true; - } - } - - return false; - } - - @Override - public boolean isSubTypeOf(DataType other) { - if (other instanceof TupleType) { - TupleType that = (TupleType) other; - - if (this.elements.size() == that.elements.size()) { - List theseElements = this.getSortedElements(); - List thoseElements = that.getSortedElements(); - for (int i = 0; i < theseElements.size(); i++) { - if (!theseElements.get(i).isSubTypeOf(thoseElements.get(i))) { - return false; - } - } - - return true; - } - } - - return super.isSubTypeOf(other); - } - - @Override - public boolean isSuperTypeOf(DataType other) { - if (other instanceof TupleType) { - TupleType that = (TupleType) other; - - if (this.elements.size() == that.elements.size()) { - List theseElements = this.getSortedElements(); - List thoseElements = that.getSortedElements(); - for (int i = 0; i < theseElements.size(); i++) { - if (!theseElements.get(i).isSuperTypeOf(thoseElements.get(i))) { - return false; - } - } - - return true; - } - } - - return super.isSuperTypeOf(other); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("tuple{"); - for (int i = 0; i < elements.size(); i++) { - if (i > 0) { - builder.append(","); - } - builder.append(elements.get(i).toString()); - } - builder.append("}"); - return builder.toString(); - } - - @Override - public String toLabel() { - StringBuilder builder = new StringBuilder(); - builder.append("tuple of "); - for (int i = 0; i < elements.size(); i++) { - if (i > 0) { - builder.append(", "); - } - builder.append(elements.get(i).toLabel()); - } - return builder.toString(); - } - - @Override - public boolean isCompatibleWith(DataType other) { - if (other instanceof ClassType) { - ClassType classType = (ClassType) other; - return this.equals(classType.getTupleType()); - } - - return super.isCompatibleWith(other); - } - - @Override - public boolean isGeneric() { - for (TupleTypeElement e : elements) { - if (e.getType().isGeneric()) { - return true; - } - } - - return false; - } - - @Override - public boolean isInstantiable(DataType callType, InstantiationContext context) { - // Call isInstantiable recursively to make sure that type parameters (if present) are bound - if (callType.equals(DataType.ANY)) { - for (var element : elements) { - if (!element.getType().isInstantiable(callType, context)) { - return false; - } - } - return true; - } - - if (callType instanceof TupleType) { - TupleType tupleType = (TupleType) callType; - if (elements.size() == tupleType.elements.size()) { - List theseElements = getSortedElements(); - List thoseElements = tupleType.getSortedElements(); - for (int i = 0; i < theseElements.size(); i++) { - if (!(theseElements - .get(i) - .getName() - .equals(thoseElements.get(i).getName()) - && theseElements - .get(i) - .getType() - .isInstantiable(thoseElements.get(i).getType(), context))) { - return false; - } - } - - return true; - } - } - - return false; - } - - @Override - public DataType instantiate(InstantiationContext context) { - if (!isGeneric()) { - return this; - } - - TupleType result = new TupleType(); - for (int i = 0; i < elements.size(); i++) { - result.addElement(new TupleTypeElement( - elements.get(i).getName(), elements.get(i).getType().instantiate(context))); - } - - return result; - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.kt new file mode 100644 index 000000000..0802205c8 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.kt @@ -0,0 +1,92 @@ +package org.hl7.cql.model + +@Suppress("TooManyFunctions") +data class TupleType +@JvmOverloads +constructor(val elements: MutableList = mutableListOf()) : BaseDataType() { + + fun addElement(element: TupleTypeElement) { + elements.add(element) + } + + fun addElements(elements: Collection) { + this.elements.addAll(elements) + } + + val sortedElements: List + get() = elements.sortedWith { l, r -> l.name.compareTo(r.name) } + + override fun isSubTypeOf(other: DataType): Boolean { + return when { + other is TupleType -> + sortedElements.size == other.sortedElements.size && + sortedElements.zip(other.sortedElements).all { it.first.isSubTypeOf(it.second) } + else -> super.isSubTypeOf(other) + } + } + + override fun isSuperTypeOf(other: DataType): Boolean { + return when { + other is TupleType -> + sortedElements.size == other.sortedElements.size && + sortedElements.zip(other.sortedElements).all { + it.first.isSuperTypeOf(it.second) + } + else -> super.isSuperTypeOf(other) + } + } + + override fun toString(): String = elements.joinToString(",", "tuple{", "}") + + override fun toLabel(): String = elements.joinToString(", ", "tuple of ") + + override fun isCompatibleWith(other: DataType): Boolean { + return if (other is ClassType) { + this == other.tupleType + } else super.isCompatibleWith(other) + } + + override val isGeneric: Boolean + get() = elements.any { it.type.isGeneric } + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + // Call isInstantiable recursively to make sure that type parameters (if present) are bound + return when (callType) { + DataType.ANY -> elements.all { it.type.isInstantiable(callType, context) } + is TupleType -> { + sortedElements.size == callType.sortedElements.size && + sortedElements.zip(callType.sortedElements).all { + it.first.name == it.second.name && + it.first.type.isInstantiable(it.second.type, context) + } + } + else -> false + } + } + + override fun instantiate(context: InstantiationContext): DataType { + return if (isGeneric) { + TupleType( + elements + .map { TupleTypeElement(it.name, it.type.instantiate(context), false) } + .toMutableList() + ) + } else this + } + + override fun hashCode(): Int { + var result = 13 + for (e in elements) { + result += (37 * e.hashCode()) + } + + return result + } + + override fun equals(other: Any?): Boolean { + return if (other is TupleType) { + sortedElements.size == other.sortedElements.size && + sortedElements.zip(other.sortedElements).all { it.first == it.second } + } else false + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.java b/Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.java deleted file mode 100644 index 4f928d246..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.hl7.cql.model; - -public class TupleTypeElement { - private String name; - private DataType type; - private boolean oneBased; - - public TupleTypeElement(String name, DataType type, boolean oneBased) { - if (name == null || name.equals("")) { - throw new IllegalArgumentException("name"); - } - - if (type == null) { - throw new IllegalArgumentException("type"); - } - - this.name = name; - this.type = type; - this.oneBased = oneBased; - } - - public TupleTypeElement(String name, DataType type) { - this(name, type, false); - } - - public String getName() { - return this.name; - } - - public DataType getType() { - return this.type; - } - - @Override - public int hashCode() { - return (17 * this.name.hashCode()) + (33 * this.type.hashCode()) + (31 * (this.oneBased ? 1 : 0)); - } - - @Override - public boolean equals(Object o) { - if (o instanceof TupleTypeElement) { - TupleTypeElement that = (TupleTypeElement) o; - return this.name.equals(that.name) && this.type.equals(that.type) && (this.oneBased == that.oneBased); - } - - return false; - } - - public boolean isSubTypeOf(TupleTypeElement that) { - return this.getName().equals(that.getName()) && this.getType().isSubTypeOf(that.getType()); - } - - public boolean isSuperTypeOf(TupleTypeElement that) { - return this.getName().equals(that.getName()) && this.getType().isSuperTypeOf(that.getType()); - } - - @Override - public String toString() { - return String.format("%s:%s", this.name, this.type.toString()); - } - - public String toLabel() { - return String.format("%s: %s", this.name, this.type.toLabel()); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.kt b/Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.kt new file mode 100644 index 000000000..db3932d73 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.kt @@ -0,0 +1,29 @@ +package org.hl7.cql.model + +import java.util.* + +data class TupleTypeElement( + val name: String, + val type: DataType, + private val oneBased: Boolean = false +) { + init { + require(name.isNotEmpty()) { "name is required" } + } + + fun isSubTypeOf(that: TupleTypeElement): Boolean { + return this.name == that.name && type.isSubTypeOf(that.type) + } + + fun isSuperTypeOf(that: TupleTypeElement): Boolean { + return this.name == that.name && type.isSuperTypeOf(that.type) + } + + override fun toString(): String { + return String.format(Locale.US, "%s:%s", this.name, type.toString()) + } + + fun toLabel(): String { + return String.format(Locale.US, "%s: %s", this.name, type.toLabel()) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.java b/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.java deleted file mode 100644 index 91b2d0653..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.hl7.cql.model; - -public class TypeParameter extends DataType { - public enum TypeParameterConstraint { - /** - * Indicates the type parameter has no constraint and be bound to any type - */ - NONE, - - /** - * Indicates the type parameter can only be bound to class types - */ - CLASS, - - /** - * Indicates the type parameter can only be bound to value types (simple types) - */ - VALUE, - - /** - * Indicates the type parameter can only be bound to tuple types - */ - TUPLE, - - /** - * Indicates the type parameter can only be bound to interval types - */ - INTERVAL, - - /** - * Indicates the type parameter can only be bound to choice types - */ - CHOICE, - - /** - * Indicates the type parameter can only be bound to the constraint type or a type derived from the constraint type - */ - TYPE - } - - public TypeParameter(String identifier) { - if (identifier == null || identifier.equals("")) { - throw new IllegalArgumentException("identifier is null"); - } - - this.identifier = identifier; - } - - public TypeParameter(String identifier, TypeParameterConstraint constraint, DataType constraintType) { - this(identifier); - this.constraint = constraint; - this.constraintType = constraintType; - } - - private String identifier; - - public String getIdentifier() { - return identifier; - } - - public TypeParameterConstraint constraint = TypeParameterConstraint.NONE; - - public TypeParameterConstraint getConstraint() { - return constraint; - } - - private DataType constraintType; - - public DataType getConstraintType() { - return constraintType; - } - - /** - * @param callType - * @return True if the given callType can be bound to this parameter (i.e. it satisfied any constraints defined for the type parameter) - */ - public boolean canBind(DataType callType) { - switch (constraint) { - case CHOICE: - return callType instanceof ChoiceType; - case TUPLE: - return callType instanceof TupleType; - case INTERVAL: - return callType instanceof IntervalType; - case CLASS: - return callType instanceof ClassType; - case VALUE: - return callType instanceof SimpleType && !callType.equals(DataType.ANY); - case TYPE: - return callType.isSubTypeOf(constraintType); - case NONE: - default: - return true; - } - } - - @Override - public int hashCode() { - return identifier.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof TypeParameter) { - TypeParameter that = (TypeParameter) o; - return this.identifier.equals(that.identifier); - } - - return false; - } - - @Override - public String toString() { - return identifier; - } - - @Override - public boolean isGeneric() { - return true; - } - - @Override - public boolean isInstantiable(DataType callType, InstantiationContext context) { - return context.isInstantiable(this, callType); - } - - @Override - public DataType instantiate(InstantiationContext context) { - return context.instantiate(this); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.kt b/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.kt new file mode 100644 index 000000000..baad6bbd9 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.kt @@ -0,0 +1,68 @@ +package org.hl7.cql.model + +data class TypeParameter +@JvmOverloads +constructor( + val identifier: String, + val constraint: TypeParameterConstraint = TypeParameterConstraint.NONE, + val constraintType: DataType? = null +) : BaseDataType() { + init { + require(identifier.isNotEmpty()) { "identifier is empty" } + } + + enum class TypeParameterConstraint { + /** Indicates the type parameter has no constraint and be bound to any type */ + NONE, + + /** Indicates the type parameter can only be bound to class types */ + CLASS, + + /** Indicates the type parameter can only be bound to value types (simple types) */ + VALUE, + + /** Indicates the type parameter can only be bound to tuple types */ + TUPLE, + + /** Indicates the type parameter can only be bound to interval types */ + INTERVAL, + + /** Indicates the type parameter can only be bound to choice types */ + CHOICE, + + /** + * Indicates the type parameter can only be bound to the constraint type or a type derived + * from the constraint type + */ + TYPE + } + + /** + * @param callType + * @return True if the given callType can be bound to this parameter (i.e. it satisfied any + * constraints defined for the type parameter) + */ + fun canBind(callType: DataType): Boolean { + return when (constraint) { + TypeParameterConstraint.CHOICE -> callType is ChoiceType + TypeParameterConstraint.TUPLE -> callType is TupleType + TypeParameterConstraint.INTERVAL -> callType is IntervalType + TypeParameterConstraint.CLASS -> callType is ClassType + TypeParameterConstraint.VALUE -> callType is SimpleType && callType != DataType.ANY + TypeParameterConstraint.TYPE -> callType.isSubTypeOf(constraintType!!) + TypeParameterConstraint.NONE -> true + } + } + + override fun toString(): String = identifier + + override val isGeneric: Boolean = true + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + return context.isInstantiable(this, callType) + } + + override fun instantiate(context: InstantiationContext): DataType { + return context.instantiate(this) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.java b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.java deleted file mode 100644 index 4366424de..000000000 --- a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.hl7.elm_modelinfo.r1.serializing; - -import java.io.*; -import java.net.URI; -import java.net.URL; -import org.hl7.elm_modelinfo.r1.ModelInfo; - -public interface ModelInfoReader { - public ModelInfo read(File src) throws IOException; - - public ModelInfo read(Reader src) throws IOException; - - public ModelInfo read(InputStream src) throws IOException; - - public ModelInfo read(URL url) throws IOException; - - public ModelInfo read(URI uri) throws IOException; - - public ModelInfo read(String string) throws IOException; -} diff --git a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.kt b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.kt new file mode 100644 index 000000000..2e30643ec --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReader.kt @@ -0,0 +1,25 @@ +@file:Suppress("PackageNaming") + +package org.hl7.elm_modelinfo.r1.serializing + +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.io.Reader +import java.net.URI +import java.net.URL +import org.hl7.elm_modelinfo.r1.ModelInfo + +interface ModelInfoReader { + @Throws(IOException::class) fun read(src: File): ModelInfo? + + @Throws(IOException::class) fun read(src: Reader): ModelInfo? + + @Throws(IOException::class) fun read(src: InputStream): ModelInfo? + + @Throws(IOException::class) fun read(url: URL): ModelInfo? + + @Throws(IOException::class) fun read(uri: URI): ModelInfo? + + @Throws(IOException::class) fun read(string: String): ModelInfo? +} diff --git a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.java b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.java deleted file mode 100644 index 90c404f56..000000000 --- a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.hl7.elm_modelinfo.r1.serializing; - -import java.util.Iterator; -import java.util.ServiceLoader; - -public class ModelInfoReaderFactory { - private ModelInfoReaderFactory() {} - - public static Iterator providers(boolean refresh) { - var loader = ServiceLoader.load(ModelInfoReaderProvider.class); - if (refresh) { - loader.reload(); - } - - return loader.iterator(); - } - - public static ModelInfoReader getReader(String contentType) { - var providers = providers(false); - if (providers.hasNext()) { - ModelInfoReaderProvider p = providers.next(); - if (providers.hasNext()) { - throw new RuntimeException(String.join( - " ", - "Multiple ModelInfoReaderProviders found on the classpath.", - "You need to remove a reference to either the 'model-jackson' or the 'model-jaxb' package")); - } - - return p.create(contentType); - } - - throw new RuntimeException(String.join( - " ", - "No ModelInfoReaderProviders found on the classpath.", - "You need to add a reference to one of the 'model-jackson' or 'model-jaxb' packages,", - "or provide your own implementation.")); - } -} diff --git a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.kt b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.kt new file mode 100644 index 000000000..6f7fe29e7 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderFactory.kt @@ -0,0 +1,45 @@ +@file:Suppress("PackageNaming") + +package org.hl7.elm_modelinfo.r1.serializing + +import java.util.* + +object ModelInfoReaderFactory { + fun providers(refresh: Boolean): Iterator { + val loader = ServiceLoader.load(ModelInfoReaderProvider::class.java) + if (refresh) { + loader.reload() + } + + return loader.iterator() + } + + @Suppress("TooGenericExceptionThrown") + @JvmStatic + fun getReader(contentType: String): ModelInfoReader? { + val providers = providers(false) + if (providers.hasNext()) { + val p = providers.next() + if (providers.hasNext()) { + throw RuntimeException( + java.lang.String.join( + " ", + "Multiple ModelInfoReaderProviders found on the classpath.", + "You need to remove a reference to either the 'model-jackson' or the 'model-jaxb' package" + ) + ) + } + + return p.create(contentType) + } + + throw RuntimeException( + java.lang.String.join( + " ", + "No ModelInfoReaderProviders found on the classpath.", + "You need to add a reference to one of the 'model-jackson' or 'model-jaxb' packages,", + "or provide your own implementation." + ) + ) + } +} diff --git a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.java b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.java deleted file mode 100644 index 5b7c87385..000000000 --- a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.hl7.elm_modelinfo.r1.serializing; - -public interface ModelInfoReaderProvider { - ModelInfoReader create(String contentType); -} diff --git a/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.kt b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.kt new file mode 100644 index 000000000..ece685b58 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/elm_modelinfo/r1/serializing/ModelInfoReaderProvider.kt @@ -0,0 +1,7 @@ +@file:Suppress("PackageNaming") + +package org.hl7.elm_modelinfo.r1.serializing + +interface ModelInfoReaderProvider { + fun create(contentType: String): ModelInfoReader? +} diff --git a/Src/java/model/src/test/java/org/hl7/cql/model/ChoiceTypeTests.java b/Src/java/model/src/test/java/org/hl7/cql/model/ChoiceTypeTests.java index dbe861089..38608148a 100644 --- a/Src/java/model/src/test/java/org/hl7/cql/model/ChoiceTypeTests.java +++ b/Src/java/model/src/test/java/org/hl7/cql/model/ChoiceTypeTests.java @@ -3,7 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.Arrays; +import java.util.Set; import org.junit.jupiter.api.Test; class ChoiceTypeTests { @@ -11,9 +11,9 @@ class ChoiceTypeTests { @Test void choiceTypeIsCompatible() { ChoiceType first = new ChoiceType( - Arrays.asList(new SimpleType("Period"), new SimpleType("Interval"), new SimpleType("DateTime"))); + Set.of(new SimpleType("Period"), new SimpleType("Interval"), new SimpleType("DateTime"))); - ChoiceType second = new ChoiceType(Arrays.asList(new SimpleType("Period"), new SimpleType("DateTime"))); + ChoiceType second = new ChoiceType(Set.of(new SimpleType("Period"), new SimpleType("DateTime"))); assertTrue(first.isCompatibleWith(second)); assertTrue(second.isCompatibleWith(first)); @@ -27,9 +27,9 @@ void choiceTypeIsCompatible() { @Test void choiceTypeIsNotCompatible() { ChoiceType first = new ChoiceType( - Arrays.asList(new SimpleType("Period"), new SimpleType("Interval"), new SimpleType("DateTime"))); + Set.of(new SimpleType("Period"), new SimpleType("Interval"), new SimpleType("DateTime"))); - ChoiceType second = new ChoiceType(Arrays.asList(new SimpleType("Integer"), new SimpleType("String"))); + ChoiceType second = new ChoiceType(Set.of(new SimpleType("Integer"), new SimpleType("String"))); assertFalse(first.isCompatibleWith(second)); assertFalse(second.isCompatibleWith(first)); diff --git a/Src/java/model/src/test/java/org/hl7/cql/model/GenericClassSignatureParserTest.java b/Src/java/model/src/test/java/org/hl7/cql/model/GenericClassSignatureParserTest.java index bead1b2bc..3364b0ede 100644 --- a/Src/java/model/src/test/java/org/hl7/cql/model/GenericClassSignatureParserTest.java +++ b/Src/java/model/src/test/java/org/hl7/cql/model/GenericClassSignatureParserTest.java @@ -12,7 +12,7 @@ class GenericClassSignatureParserTest { @Test void parseTest1() { - GenericClassSignatureParser genericClassSignatureParser = new GenericClassSignatureParser("MyType", null); + GenericClassSignatureParser genericClassSignatureParser = new GenericClassSignatureParser("MyType"); if (genericClassSignatureParser.isValidGenericSignature()) { ClassType signature = genericClassSignatureParser.parseGenericSignature(); assertThat(signature.getName(), is("MyType")); @@ -26,7 +26,7 @@ void parseTest1() { @Test void parseTest2() { - ClassType collectionType = new ClassType("Collection", null, null); + ClassType collectionType = new ClassType("Collection", null); Map resolvedTypes = new HashMap<>(); resolvedTypes.put("Collection", collectionType); GenericClassSignatureParser genericClassSignatureParser = @@ -50,8 +50,8 @@ void parseTest2() { @Test void parseTest3() { - ClassType collectionType = new ClassType("Collection", null, null); - ClassType objectType = new ClassType("Object", null, null); + ClassType collectionType = new ClassType("Collection", null); + ClassType objectType = new ClassType("Object", null); Map resolvedTypes = new HashMap<>(); resolvedTypes.put("Collection", collectionType); resolvedTypes.put("Object", objectType); @@ -82,7 +82,7 @@ void parseTest3() { @Test void parseTest4() { try { - ClassType collectionType = new ClassType("Collection", null, null); + ClassType collectionType = new ClassType("Collection", null); Map resolvedTypes = new HashMap<>(); resolvedTypes.put("Collection", collectionType); GenericClassSignatureParser genericClassSignatureParser = @@ -96,10 +96,10 @@ void parseTest4() { @Test void parseTest5() { - ClassType objectType = new ClassType("Object", null, null); - ClassType listType = new ClassType("List", null, null); + ClassType objectType = new ClassType("Object", null); + ClassType listType = new ClassType("List", null); listType.addGenericParameter(new TypeParameter("T")); - listType.getElements().add(new ClassTypeElement("elements", new TypeParameter("T"))); + listType.getElements().add(new ClassTypeElement("elements", new TypeParameter("T"), false, false, null)); Map resolvedTypes = new HashMap<>(); resolvedTypes.put("Object", objectType); resolvedTypes.put("List", listType); @@ -125,16 +125,16 @@ void parseTest5() { @Test void parseTest6() { - ClassType objectType = new ClassType("Object", null, null); - SimpleType stringType = new SimpleType("String", null); - ClassType listType = new ClassType("List", null, null); + ClassType objectType = new ClassType("Object", null); + SimpleType stringType = new SimpleType("String"); + ClassType listType = new ClassType("List", null); listType.addGenericParameter(new TypeParameter("T")); - listType.getElements().add(new ClassTypeElement("elements", new TypeParameter("T"))); - ClassType mapType = new ClassType("Map", null, null); + listType.getElements().add(new ClassTypeElement("elements", new TypeParameter("T"), false, false, null)); + ClassType mapType = new ClassType("Map", null); mapType.addGenericParameter(new TypeParameter("K")); mapType.addGenericParameter(new TypeParameter("V")); - mapType.addElement(new ClassTypeElement("keys", new TypeParameter("K"))); - mapType.addElement(new ClassTypeElement("values", new TypeParameter("V"))); + mapType.addElement(new ClassTypeElement("keys", new TypeParameter("K"), false, false, null)); + mapType.addElement(new ClassTypeElement("values", new TypeParameter("V"), false, false, null)); Map resolvedTypes = new HashMap<>(); resolvedTypes.put("Object", objectType); resolvedTypes.put("String", stringType); @@ -162,16 +162,16 @@ void parseTest6() { @Test void parseTest7() { - SimpleType integerType = new SimpleType("Integer", null); - SimpleType stringType = new SimpleType("String", null); - ClassType listType = new ClassType("List", null, null); + SimpleType integerType = new SimpleType("Integer"); + SimpleType stringType = new SimpleType("String"); + ClassType listType = new ClassType("List", null); listType.addGenericParameter(new TypeParameter("T")); - listType.getElements().add(new ClassTypeElement("elements", new TypeParameter("T"))); - ClassType mapType = new ClassType("Map", null, null); + listType.getElements().add(new ClassTypeElement("elements", new TypeParameter("T"), false, false, null)); + ClassType mapType = new ClassType("Map", null); mapType.addGenericParameter(new TypeParameter("K")); mapType.addGenericParameter(new TypeParameter("V")); - mapType.addElement(new ClassTypeElement("keys", new TypeParameter("K"))); - mapType.addElement(new ClassTypeElement("values", new TypeParameter("V"))); + mapType.addElement(new ClassTypeElement("keys", new TypeParameter("K"), false, false, null)); + mapType.addElement(new ClassTypeElement("values", new TypeParameter("V"), false, false, null)); Map resolvedTypes = new HashMap<>(); resolvedTypes.put("Integer", integerType); resolvedTypes.put("String", stringType); diff --git a/Src/java/tools/xsd-to-modelinfo/src/main/java/org/cqframework/cql/tools/xsd2modelinfo/ModelImporter.java b/Src/java/tools/xsd-to-modelinfo/src/main/java/org/cqframework/cql/tools/xsd2modelinfo/ModelImporter.java index f4cb8b06e..c2a11b124 100644 --- a/Src/java/tools/xsd-to-modelinfo/src/main/java/org/cqframework/cql/tools/xsd2modelinfo/ModelImporter.java +++ b/Src/java/tools/xsd-to-modelinfo/src/main/java/org/cqframework/cql/tools/xsd2modelinfo/ModelImporter.java @@ -182,7 +182,7 @@ private ClassInfo toClassInfo(ClassType dataType) { cie.setTypeSpecifier(elementTypeSpecifier); } } - if (element.isProhibited()) { + if (element.getProhibited()) { cie.setProhibited(true); } result.getElement().add(cie); @@ -535,8 +535,8 @@ else if (content instanceof XmlSchemaSimpleContentRestriction) { classType.addElement(new ClassTypeElement( name.toString(), element.getType(), - element.isProhibited(), - element.isOneBased(), + element.getProhibited(), + element.getOneBased(), null)); } }