From 36824af63cd8ab42c252139ee92a3fd47ece5524 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Fri, 6 Dec 2024 12:32:04 -0700 Subject: [PATCH 01/27] ChoiceType --- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 144 ++++++++------- .../cql/cql2elm/SystemMethodResolver.kt | 10 +- .../cql/cql2elm/model/ModelImporter.kt | 15 +- .../CqlPreprocessorElmCommonVisitor.kt | 2 +- .../cql/elm/requirements/TypeResolver.java | 6 +- .../java/org/hl7/cql/model/ChoiceType.java | 168 ------------------ .../main/java/org/hl7/cql/model/ChoiceType.kt | 102 +++++++++++ .../org/hl7/cql/model/ChoiceTypeTests.java | 10 +- .../tools/xsd2modelinfo/ModelImporter.java | 3 +- 9 files changed, 204 insertions(+), 256 deletions(-) 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 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..fb8f86c3e 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 @@ -430,30 +430,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) + } } } @@ -469,10 +473,7 @@ class LibraryBuilder( 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("Could not resolve model name %s", modelName) } return getModel(usingDef) @@ -2238,8 +2239,8 @@ class LibraryBuilder( if (compatibleType != null) { return compatibleType } - if (!second.isSubTypeOf(first)) { - return ChoiceType(listOf(first, second)) + if (!second.isSubTypeOf(first) && first != null) { + return ChoiceType(listOf(first, second).flattenChoices()) } // The above construction of a choice type guarantees this will never be hit @@ -2538,7 +2539,11 @@ class LibraryBuilder( // The result type is a choice of all the resolved types if (resultTypes.size > 1) { - return PropertyResolution(ChoiceType(resultTypes), name!!, resultTargetMaps) + return PropertyResolution( + ChoiceType(resultTypes.flattenChoices()), + name!!, + resultTargetMaps + ) } if (resultTypes.size == 1) { return PropertyResolution( @@ -3008,43 +3013,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("[") 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..4d2a9fd57 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 @@ -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,12 +300,12 @@ 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]) } else { - children.resultType = ListType(ChoiceType(dataTypes)) + children.resultType = ListType(ChoiceType(dataTypes.toList().flattenChoices())) } children } @@ -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/model/ModelImporter.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/model/ModelImporter.kt index 3c52412dd..02e70dcdb 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 @@ -22,6 +22,7 @@ import org.hl7.cql.model.TupleType import org.hl7.cql.model.TupleTypeElement import org.hl7.cql.model.TypeParameter import org.hl7.cql.model.TypeParameter.TypeParameterConstraint +import org.hl7.cql.model.flattenChoices import org.hl7.elm_modelinfo.r1.BoundParameterTypeSpecifier import org.hl7.elm_modelinfo.r1.ChoiceTypeInfo import org.hl7.elm_modelinfo.r1.ChoiceTypeSpecifier @@ -242,12 +243,12 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { } 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) + return ChoiceType(choices.flattenChoices()) } return null @@ -694,17 +695,17 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { } 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) + return ChoiceType(types.flattenChoices()) } /** 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..cfc99b96a 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 @@ -175,7 +175,7 @@ abstract class CqlPreprocessorElmCommonVisitor( if (includeDeprecatedElements) { result.type.addAll(typeSpecifiers) } - val choiceType = ChoiceType(types) + val choiceType = ChoiceType(types.flattenChoices()) result.resultType = choiceType return result } 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..c0c07cbc8 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 @@ -1,5 +1,7 @@ package org.cqframework.cql.elm.requirements; +import static org.hl7.cql.model.ChoiceTypeKt.flattenChoices; + import java.util.ArrayList; import javax.xml.namespace.QName; import org.cqframework.cql.cql2elm.LibraryManager; @@ -151,11 +153,11 @@ 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)); } - return new ChoiceType(choiceTypes); + return new ChoiceType(flattenChoices(choiceTypes)); } public DataType resolveTypeName(String modelName, String typeName) { 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..d2b6640b6 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.kt @@ -0,0 +1,102 @@ +package org.hl7.cql.model + +/** Created by Bryn on 11/8/2016. */ +data class ChoiceType(val types: Set) : DataType() { + 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." + } + } + + override fun isSubTypeOf(other: DataType): Boolean { + // Choice types do not follow the is-a relationship, they use the is-compatible relationship + // instead, defined + // using subset/superset + return super.isSubTypeOf(other) + } + + fun isSubSetOf(other: ChoiceType): Boolean { + for (type in types) { + var currentIsSubType = false + for (otherType in other.types) { + currentIsSubType = type.isSubTypeOf(otherType) + if (currentIsSubType) { + break + } + } + + if (!currentIsSubType) { + return false + } + } + + return true + } + + override fun isSuperTypeOf(other: DataType): Boolean { + return super.isSuperTypeOf(other) + } + + 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 { + val sb = StringBuilder() + sb.append("choice<") + var first = true + for (type in types) { + if (first) { + first = false + } else { + sb.append(",") + } + sb.append(type.toString()) + } + sb.append(">") + return sb.toString() + } + + override fun isGeneric(): Boolean { + // It hardly makes sense for a choice type to have generics.... + // ignoring in instantiation semantics for now + return 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 + } +} + +fun Iterable.flattenChoices(): Set { + return this.flatMap { + if (it is ChoiceType) { + it.types + } else { + setOf(it) + } + } + .toSet() +} 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/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..356ec2c17 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 @@ -1,6 +1,7 @@ package org.cqframework.cql.tools.xsd2modelinfo; import static org.cqframework.cql.tools.xsd2modelinfo.ModelImporterOptions.ChoiceTypePolicy.USE_CHOICE; +import static org.hl7.cql.model.ChoiceTypeKt.flattenChoices; import jakarta.xml.bind.JAXB; import java.io.IOException; @@ -636,7 +637,7 @@ private void resolveClassTypeElements(XmlSchemaParticle particle, List Date: Fri, 6 Dec 2024 13:21:02 -0700 Subject: [PATCH 02/27] ClassTypeElement --- .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 30 ++--- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 2 +- .../cql/cql2elm/model/ModelImporter.kt | 16 +-- .../java/org/hl7/cql/model/ClassType.java | 32 ++--- .../org/hl7/cql/model/ClassTypeElement.java | 110 ------------------ .../org/hl7/cql/model/ClassTypeElement.kt | 36 ++++++ .../model/GenericClassSignatureParser.java | 6 +- .../GenericClassSignatureParserTest.java | 14 +-- .../tools/xsd2modelinfo/ModelImporter.java | 6 +- 9 files changed, 85 insertions(+), 167 deletions(-) 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 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..163ec510e 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 @@ -363,9 +363,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 +378,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()) @@ -3452,10 +3448,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? @@ -3998,10 +3992,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) } @@ -4208,7 +4200,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 +4227,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 +4248,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)!!, @@ -4607,7 +4595,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/LibraryBuilder.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.kt index fb8f86c3e..d2335126b 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 @@ -2463,7 +2463,7 @@ class LibraryBuilder( } else { for (e: ClassTypeElement in classType.elements) { if ((e.name == identifier)) { - if (e.isProhibited) { + if (e.prohibited) { throw IllegalArgumentException( String.format( "Element %s cannot be referenced because it is marked prohibited in type %s.", 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 02e70dcdb..561b115f5 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 @@ -73,17 +73,13 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { .withId(requiredModel.name) .withVersion(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 - } + modelIndex["System"] = systemModel } } @@ -497,7 +493,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 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 index fbe2ff11a..37098f498 100644 --- 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 @@ -11,7 +11,7 @@ public ClassType( Collection parameters) { super(baseType); - if (name == null || name.equals("")) { + if (name == null || name.isEmpty()) { throw new IllegalArgumentException("name is null"); } @@ -49,11 +49,11 @@ public String getName() { } 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 (this.getName() != null) { + int qualifierIndex = this.getName() + .indexOf('.'); // TODO Should this not be the last occurrence rather than the first occurrence? if (qualifierIndex > 0) { - return this.name.substring(0, qualifierIndex); + return this.getName().substring(0, qualifierIndex); } } @@ -61,15 +61,15 @@ public String getNamespace() { } 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 (this.getName() != null) { + int qualifierIndex = this.getName() + .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.getName().substring(qualifierIndex + 1); } } - return this.name; + return this.getName(); } private String identifier; @@ -353,14 +353,14 @@ private List getSortedElements() { @Override public int hashCode() { - return this.name.hashCode(); + return this.getName().hashCode(); } @Override public boolean equals(Object o) { if (o instanceof ClassType) { ClassType that = (ClassType) o; - return this.name.equals(that.name); + return this.getName().equals(that.getName()); } return false; @@ -368,12 +368,12 @@ public boolean equals(Object o) { @Override public String toString() { - return this.name; + return this.getName(); } @Override public String toLabel() { - return this.label == null ? this.name : this.label; + return this.label == null ? this.getName() : this.label; } private TupleType tupleType; @@ -394,7 +394,7 @@ private void addTupleElements(ClassType classType, LinkedHashMap resolvedTypes = new HashMap<>(); resolvedTypes.put("Object", objectType); resolvedTypes.put("List", listType); @@ -129,12 +129,12 @@ void parseTest6() { SimpleType stringType = new SimpleType("String", null); ClassType listType = new ClassType("List", null, 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)); ClassType mapType = new ClassType("Map", null, 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); @@ -166,12 +166,12 @@ void parseTest7() { SimpleType stringType = new SimpleType("String", null); ClassType listType = new ClassType("List", null, 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)); ClassType mapType = new ClassType("Map", null, 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 356ec2c17..6a6b36c4d 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 @@ -183,7 +183,7 @@ private ClassInfo toClassInfo(ClassType dataType) { cie.setTypeSpecifier(elementTypeSpecifier); } } - if (element.isProhibited()) { + if (element.getProhibited()) { cie.setProhibited(true); } result.getElement().add(cie); @@ -536,8 +536,8 @@ else if (content instanceof XmlSchemaSimpleContentRestriction) { classType.addElement(new ClassTypeElement( name.toString(), element.getType(), - element.isProhibited(), - element.isOneBased(), + element.getProhibited(), + element.getOneBased(), null)); } } From 8954f37af8995121ebd8fef756e491ceb745604b Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Fri, 6 Dec 2024 22:02:35 -0500 Subject: [PATCH 03/27] InstantiationContext --- .../cql/cql2elm/model/InstantiationContextImpl.kt | 6 +++--- .../org/hl7/cql/model/InstantiationContext.java | 13 ------------- .../java/org/hl7/cql/model/InstantiationContext.kt | 13 +++++++++++++ 3 files changed, 16 insertions(+), 16 deletions(-) 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 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/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 +} From 27f89dfbf52735a6996cb63ac1f308d9fa6262d4 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Fri, 6 Dec 2024 23:35:49 -0500 Subject: [PATCH 04/27] TupleType, TupleTypeelement, SearchType, Relationship --- .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 6 +- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 2 +- .../cqframework/cql/cql2elm/LibraryManager.kt | 2 +- .../cql/cql2elm/model/ModelImporter.kt | 6 +- .../model/ResolvedIdentifierContext.kt | 1 + .../cql2elm/preprocessor/CqlPreprocessor.kt | 5 +- .../CqlPreprocessorElmCommonVisitor.kt | 27 ++++---- .../cql/elm/requirements/TypeResolver.java | 2 +- .../fhir/DataRequirementsProcessorTest.java | 8 ++- .../java/org/hl7/cql/model/ClassType.java | 2 +- .../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/TupleType.java | 4 +- .../org/hl7/cql/model/TupleTypeElement.java | 65 ------------------- .../org/hl7/cql/model/TupleTypeElement.kt | 29 +++++++++ 17 files changed, 76 insertions(+), 156 deletions(-) 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/TupleTypeElement.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TupleTypeElement.kt 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 163ec510e..6cc36268d 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 @@ -123,7 +123,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 } @@ -158,7 +158,7 @@ class Cql2ElmVisitor( 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 +173,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( 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 d2335126b..497e8da30 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 @@ -514,7 +514,7 @@ class LibraryBuilder( ) } - 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) 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..7cc94a532 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 @@ -135,7 +135,7 @@ constructor( ) val compiler = CqlCompiler( - namespaceManager.getNamespaceInfoFromUri(libraryIdentifier.system), + libraryIdentifier.system?.let { namespaceManager.getNamespaceInfoFromUri(it) }, libraryIdentifier, this ) 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 561b115f5..06df66ff5 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 @@ -232,7 +232,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { val element = TupleTypeElement( specifierElement.name, - resolveTypeSpecifier(specifierElement.elementType) + resolveTypeSpecifier(specifierElement.elementType)!! ) tupleType.addElement(element) } @@ -399,7 +399,7 @@ 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 } @@ -584,7 +584,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 { 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 cfc99b96a..2366128f4 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 } 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 c0c07cbc8..6906c532c 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 @@ -138,7 +138,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; 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..0e4b6c630 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 @@ -1933,7 +1933,13 @@ private void assertEqualToExpectedModuleDefinitionLibrary( // outputModuleDefinitionLibrary(actualModuleDefinitionLibrary); actualModuleDefinitionLibrary.setDate(null); expectedModuleDefinitionLibrary.setDate(null); - assertTrue(actualModuleDefinitionLibrary.equalsDeep(expectedModuleDefinitionLibrary)); + + var jsonExpected = parser.encodeResourceToString(expectedModuleDefinitionLibrary); + var jsonActual = parser.encodeResourceToString(actualModuleDefinitionLibrary); + + assertEquals(jsonExpected, jsonActual); + + // assertTrue(actualModuleDefinitionLibrary.equalsDeep(expectedModuleDefinitionLibrary)); } @Test 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 index 37098f498..d36e464d9 100644 --- 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 @@ -395,7 +395,7 @@ private void addTupleElements(ClassType classType, LinkedHashMap 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/TupleType.java b/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.java index 473d6c9d2..448d9194c 100644 --- 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 @@ -35,7 +35,7 @@ public void addElements(Collection elements) { private List getSortedElements() { if (sortedElements == null) { sortedElements = new ArrayList<>(elements); - Collections.sort(sortedElements, (left, right) -> left.getName().compareTo(right.getName())); + sortedElements.sort((left, right) -> left.getName().compareTo(right.getName())); } return sortedElements; @@ -208,7 +208,7 @@ public DataType instantiate(InstantiationContext context) { 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))); + elements.get(i).getName(), elements.get(i).getType().instantiate(context), false)); } return result; 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()) + } +} From e4497d1ef5386c845c5cfa3bd929abedf642a2fb Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Sat, 7 Dec 2024 09:35:53 -0500 Subject: [PATCH 05/27] NamespaceInfo --- .../fhir/DataRequirementsProcessorTest.java | 1 + .../Library-BSElements-data-requirements.json | 10 ++-- .../java/org/hl7/cql/model/NamespaceInfo.java | 48 ------------------- .../java/org/hl7/cql/model/NamespaceInfo.kt | 11 +++++ 4 files changed, 17 insertions(+), 53 deletions(-) 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 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 0e4b6c630..7fdae08c6 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 @@ -1934,6 +1934,7 @@ private void assertEqualToExpectedModuleDefinitionLibrary( actualModuleDefinitionLibrary.setDate(null); expectedModuleDefinitionLibrary.setDate(null); + parser.setPrettyPrint(true); var jsonExpected = parser.encodeResourceToString(expectedModuleDefinitionLibrary); var jsonActual = parser.encodeResourceToString(actualModuleDefinitionLibrary); 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/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..fd2641f09 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/NamespaceInfo.kt @@ -0,0 +1,11 @@ +package org.hl7.cql.model + +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("%s: %s", name, uri) + } +} From 16a4a45b6b743aa3d09d90f69bdc95595b3385a5 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Sat, 7 Dec 2024 23:00:08 -0500 Subject: [PATCH 06/27] ModelContext --- .../cqframework/cql/cql2elm/model/Model.kt | 4 +- .../cql/cql2elm/model/ModelImporter.kt | 4 +- .../java/org/hl7/cql/model/ModelContext.java | 43 ------------------- .../java/org/hl7/cql/model/ModelContext.kt | 9 ++++ 4 files changed, 13 insertions(+), 47 deletions(-) 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 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..f7cb30678 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 @@ -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 06df66ff5..ff6ca77d0 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 @@ -119,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 ) @@ -136,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 ) 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 +) From 3ee1968d7bccadfa7448d70d3553d81222208a35 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Sun, 8 Dec 2024 07:38:36 -0500 Subject: [PATCH 07/27] ModelIdentifier --- .../fhir/npm/NpmModelInfoProvider.java | 6 +- .../fhir/npm/NpmPackageManagerTests.java | 7 +- .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 9 +- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 11 +- .../cqframework/cql/cql2elm/LibraryManager.kt | 2 +- .../cqframework/cql/cql2elm/ModelManager.kt | 51 +--- .../cql/cql2elm/model/ModelImporter.kt | 11 +- .../CqlPreprocessorElmCommonVisitor.kt | 5 +- .../fhir/model/TestDstu2ModelResolver.java | 2 +- .../fhir/model/TestDstu3ModelResolver.java | 2 +- .../fhir/model/TestR4ModelResolver.java | 4 +- .../org/hl7/cql/model/ModelIdentifier.java | 236 ------------------ .../java/org/hl7/cql/model/ModelIdentifier.kt | 14 ++ .../java/org/hl7/cql/model/NamespaceInfo.kt | 4 +- 14 files changed, 49 insertions(+), 315 deletions(-) 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 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 6cc36268d..e9e62abb2 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 @@ -144,17 +144,14 @@ 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) } 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 497e8da30..3499f1cc4 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 @@ -467,7 +467,7 @@ 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) @@ -481,10 +481,11 @@ class LibraryBuilder( 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 ) } 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 7cc94a532..966068c5e 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 @@ -32,7 +32,7 @@ constructor( READ_WRITE } - var namespaceManager = modelManager.getNamespaceManager() + var namespaceManager = modelManager.namespaceManager var compiledLibraries: MutableMap = libraryCache ?: HashMap() val librarySourceLoader: LibrarySourceLoader = PriorityLibrarySourceLoader() 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..3fdfededb 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 @@ -203,10 +171,6 @@ class ModelManager { } } - 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, 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 ff6ca77d0..818321e2f 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 @@ -68,17 +68,18 @@ 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, + ) ) modelIndex[requiredModel.name] = model } // Ensure System model is registered if (!modelIndex.containsKey("System")) { - val systemModel = modelManager.resolveModel(ModelIdentifier().withId("System")) + val systemModel = modelManager.resolveModel(ModelIdentifier("System")) modelIndex["System"] = systemModel } } 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 2366128f4..a03947fce 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 @@ -252,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/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/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..97a9a1392 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ModelIdentifier.kt @@ -0,0 +1,14 @@ +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 +) 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 index fd2641f09..de5694bbd 100644 --- 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 @@ -1,11 +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("%s: %s", name, uri) + return String.format(Locale.US, "%s: %s", name, uri) } } From 736c5e46ccdf625d037aba833c94d266f2717698 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Sun, 8 Dec 2024 08:58:42 -0500 Subject: [PATCH 08/27] ModelInfoProvider, NamespaceAware, ModelInfoReader, ModelInfoReaderFactory, ModelInfoReaderProvider --- .../cql/cql2elm/DefaultLibrarySourceLoader.kt | 16 +++---- .../cql2elm/DefaultLibrarySourceProvider.kt | 21 +++++---- .../cql/cql2elm/DefaultModelInfoProvider.kt | 15 ++++--- .../cql/cql2elm/LibrarySourceLoader.kt | 8 ++-- .../cql/cql2elm/ModelInfoLoader.kt | 35 +++++---------- .../cqframework/cql/cql2elm/ModelManager.kt | 2 +- .../org/cqframework/cql/cql2elm/PathAware.kt | 2 +- .../cql2elm/PriorityLibrarySourceLoader.kt | 23 +++++----- .../java/org/hl7/cql/model/ModelIdentifier.kt | 6 ++- .../org/hl7/cql/model/ModelInfoProvider.java | 7 --- .../org/hl7/cql/model/ModelInfoProvider.kt | 7 +++ .../org/hl7/cql/model/NamespaceAware.java | 5 --- .../java/org/hl7/cql/model/NamespaceAware.kt | 5 +++ .../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 +++ 19 files changed, 151 insertions(+), 141 deletions(-) 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/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/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/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..ee0c6d6a1 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 @@ -15,20 +15,19 @@ 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?) { + override fun setPath(path: Path) { if (path == null || !path.toFile().isDirectory) { throw IllegalArgumentException( @Suppress("ImplicitDefaultLocale") @@ -60,8 +59,7 @@ internal class DefaultLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, return providers } - override fun getLibrarySource(libraryIdentifier: VersionedIdentifier?): InputStream { - require(libraryIdentifier != null) { "libraryIdentifier is null." } + override fun getLibrarySource(libraryIdentifier: VersionedIdentifier): InputStream { require(!libraryIdentifier.id.isNullOrEmpty()) { "libraryIdentifier Id is null." } var source: InputStream? = null for (provider: LibrarySourceProvider in getProviders()) { 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/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..2906c9c0f 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) @@ -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 3fdfededb..fb2a7d36a 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 @@ -167,7 +167,7 @@ class ModelManager { modelInfoLoader = ModelInfoLoader() modelInfoLoader!!.setNamespaceManager(namespaceManager) if (path != null) { - modelInfoLoader!!.setPath(path) + modelInfoLoader!!.setPath(path!!) } } 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..11a4b1921 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,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,23 +17,22 @@ class PriorityLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, PathAwa 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 (provider is NamespaceAware && namespaceManager != null) { + 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()) { @@ -58,12 +59,12 @@ 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) 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 index 97a9a1392..e70d4d3cc 100644 --- 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 @@ -11,4 +11,8 @@ 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/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/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? +} From c8f8c7c4420fee89c9bb3505203c63ca7f22823a Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Sun, 8 Dec 2024 09:02:36 -0500 Subject: [PATCH 09/27] small clean up --- .../cql2elm/PriorityLibrarySourceLoader.kt | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) 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 11a4b1921..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 @@ -10,7 +10,7 @@ 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 { @@ -18,7 +18,7 @@ class PriorityLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, PathAwa private var initialized = false override fun registerProvider(provider: LibrarySourceProvider) { - if (provider is NamespaceAware && namespaceManager != null) { + if (namespaceManager != null && provider is NamespaceAware) { provider.setNamespaceManager(namespaceManager!!) } @@ -37,7 +37,7 @@ class PriorityLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, PathAwa this.path = path for (provider in getProviders()) { if (provider is PathAware) { - (provider as PathAware).setPath(path) + provider.setPath(path) } } } @@ -67,10 +67,9 @@ class PriorityLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, PathAwa 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 } @@ -84,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." - } - } } From 7375dba6bc55ddb306b5982d4e85417e713c4fea Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Sun, 8 Dec 2024 09:42:15 -0500 Subject: [PATCH 10/27] NamespaceManager, SystemModelInfoProvider --- .../cql/cql2elm/DefaultLibrarySourceLoader.kt | 17 ++- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 2 +- .../org/hl7/cql/model/NamespaceManager.java | 109 ------------------ .../org/hl7/cql/model/NamespaceManager.kt | 74 ++++++++++++ .../cql/model/SystemModelInfoProvider.java | 37 ------ .../hl7/cql/model/SystemModelInfoProvider.kt | 30 +++++ 6 files changed, 113 insertions(+), 156 deletions(-) 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/SystemModelInfoProvider.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/SystemModelInfoProvider.kt 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 ee0c6d6a1..2720195c2 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 @@ -28,16 +30,14 @@ internal class DefaultLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, 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) - ) + 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,14 +60,13 @@ internal class DefaultLibrarySourceLoader : LibrarySourceLoader, NamespaceAware, } override fun getLibrarySource(libraryIdentifier: VersionedIdentifier): InputStream { - require(!libraryIdentifier.id.isNullOrEmpty()) { "libraryIdentifier Id is null." } 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 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 3499f1cc4..cef1a3f7e 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 @@ -482,7 +482,7 @@ class LibraryBuilder( fun getModel(usingDef: UsingDef): Model { return getModel( ModelIdentifier( - id = NamespaceManager.getNamePart(usingDef.uri), + id = NamespaceManager.getNamePart(usingDef.uri)!!, system = NamespaceManager.getUriPart(usingDef.uri), version = usingDef.version ), 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/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 + } +} From eff6bd281505acde31d712a2024ea8e5c9bbdbe3 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Sun, 8 Dec 2024 09:47:44 -0500 Subject: [PATCH 11/27] clean up --- .../src/main/java/org/hl7/cql/model/ChoiceType.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) 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 index d2b6640b6..d53ab20ec 100644 --- 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 @@ -90,13 +90,5 @@ data class ChoiceType(val types: Set) : DataType() { } } -fun Iterable.flattenChoices(): Set { - return this.flatMap { - if (it is ChoiceType) { - it.types - } else { - setOf(it) - } - } - .toSet() -} +fun Iterable.flattenChoices(): Set = + flatMap { (it as? ChoiceType)?.types ?: setOf(it) }.toSet() From b45e190162d600d6e677c9b856cce6227a863d43 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Mon, 9 Dec 2024 09:07:36 -0500 Subject: [PATCH 12/27] NamedType, InvalidRedeclarationException --- .../model/InvalidRedeclarationException.java | 17 ------------- .../model/InvalidRedeclarationException.kt | 24 +++++++++++++++++++ .../java/org/hl7/cql/model/NamedType.java | 11 --------- .../main/java/org/hl7/cql/model/NamedType.kt | 8 +++++++ 4 files changed, 32 insertions(+), 28 deletions(-) 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/NamedType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/NamedType.kt 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/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? +} From 3ba203ddf4445eab24b5eab60464c7fb43ed48f4 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Mon, 9 Dec 2024 19:06:44 -0500 Subject: [PATCH 13/27] Break out DataType interface --- .../cql/cql2elm/SystemFunctionResolver.kt | 93 ++++++++++--------- .../java/org/hl7/cql/model/BaseDataType.java | 82 ++++++++++++++++ .../main/java/org/hl7/cql/model/ChoiceType.kt | 2 +- .../java/org/hl7/cql/model/ClassType.java | 2 +- .../main/java/org/hl7/cql/model/DataType.java | 83 +++-------------- .../java/org/hl7/cql/model/IntervalType.java | 2 +- .../main/java/org/hl7/cql/model/ListType.java | 2 +- .../java/org/hl7/cql/model/SimpleType.java | 2 +- .../java/org/hl7/cql/model/TupleType.java | 2 +- .../java/org/hl7/cql/model/TypeParameter.java | 2 +- 10 files changed, 150 insertions(+), 122 deletions(-) create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java 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..5432218bf 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 @@ -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/model/src/main/java/org/hl7/cql/model/BaseDataType.java b/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java new file mode 100644 index 000000000..68f2efeed --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java @@ -0,0 +1,82 @@ +package org.hl7.cql.model; + +public abstract class BaseDataType implements DataType { + public BaseDataType() { + this(DataType.ANY); + } + + public BaseDataType(DataType baseType) { + this.baseType = baseType != null ? baseType : DataType.ANY; + } + + private DataType baseType; + + @Override + public DataType getBaseType() { + return baseType; + } + + @Override + public String toLabel() { + return toString(); + } + + @Override + public boolean isSubTypeOf(DataType other) { + DataType currentType = this; + while (currentType != null) { + if (currentType.equals(other)) { + return true; + } + currentType = currentType.getBaseType(); + } + + return false; + } + + @Override + public boolean isSuperTypeOf(DataType other) { + while (other != null) { + if (equals(other)) { + return true; + } + other = other.getBaseType(); + } + + return false; + } + + /** + * @param other + * @return The first supertype of this type that is also a supertype of other + */ + @Override + public DataType getCommonSuperTypeOf(DataType other) { + DataType currentType = this; + while (currentType != null) { + if (currentType.isSuperTypeOf(other)) { + return currentType; + } + currentType = currentType.getBaseType(); + } + + 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. + @Override + 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 + } +} 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 index d53ab20ec..8257d7438 100644 --- 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 @@ -1,7 +1,7 @@ package org.hl7.cql.model /** Created by Bryn on 11/8/2016. */ -data class ChoiceType(val types: Set) : DataType() { +data class ChoiceType(val types: Set) : BaseDataType() { init { require(types.isNotEmpty()) { "A choice type must have at least one type." } require(types.none { it is ChoiceType }) { 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 index d36e464d9..f562b8e02 100644 --- 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 @@ -2,7 +2,7 @@ import java.util.*; -public class ClassType extends DataType implements NamedType { +public class ClassType extends BaseDataType implements NamedType { public ClassType( String name, 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 index 9a5841e82..c88cf9df5 100644 --- 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 @@ -1,87 +1,26 @@ 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 interface DataType { + DataType getBaseType(); - public DataType(DataType baseType) { - this.baseType = baseType == null ? DataType.ANY : baseType; - } + String toLabel(); - private DataType baseType; + boolean isSubTypeOf(DataType other); - public DataType getBaseType() { - return baseType; - } + boolean isSuperTypeOf(DataType other); - 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; - } + DataType getCommonSuperTypeOf(DataType other); // 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 - } + boolean isCompatibleWith(DataType other); - public abstract boolean isGeneric(); + boolean isGeneric(); - public abstract boolean isInstantiable(DataType callType, InstantiationContext context); + boolean isInstantiable(DataType callType, InstantiationContext context); - public abstract DataType instantiate(InstantiationContext context); + DataType instantiate(InstantiationContext context); - public static final SimpleType ANY = new SimpleType("System.Any"); + SimpleType ANY = new SimpleType("System.Any"); } 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 index 273859720..c2faed546 100644 --- 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 @@ -1,6 +1,6 @@ package org.hl7.cql.model; -public class IntervalType extends DataType { +public class IntervalType extends BaseDataType { private DataType pointType; public IntervalType(DataType pointType) { 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 index 13dd6b7e3..bcadfad40 100644 --- 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 @@ -1,6 +1,6 @@ package org.hl7.cql.model; -public class ListType extends DataType { +public class ListType extends BaseDataType { private DataType elementType; public ListType(DataType elementType) { 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 index a1d487812..9f73efc6b 100644 --- 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 @@ -1,6 +1,6 @@ package org.hl7.cql.model; -public class SimpleType extends DataType implements NamedType { +public class SimpleType extends BaseDataType implements NamedType { private String name; public SimpleType(String name, DataType baseType) { 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 index 448d9194c..b9131f7a5 100644 --- 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 @@ -2,7 +2,7 @@ import java.util.*; -public class TupleType extends DataType { +public class TupleType extends BaseDataType { private List elements = new ArrayList(); private List sortedElements = null; 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 index 91b2d0653..3226760ab 100644 --- 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 @@ -1,6 +1,6 @@ package org.hl7.cql.model; -public class TypeParameter extends DataType { +public class TypeParameter extends BaseDataType { public enum TypeParameterConstraint { /** * Indicates the type parameter has no constraint and be bound to any type From 6ac777362433bb785860b7470a17cabfa23a0be5 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 10:48:00 -0500 Subject: [PATCH 14/27] DataType --- .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 18 ++++++------ .../cqframework/cql/cql2elm/LibraryBuilder.kt | 7 +++-- .../cql/cql2elm/SystemFunctionResolver.kt | 4 +-- .../cql/cql2elm/SystemMethodResolver.kt | 3 +- .../cql/cql2elm/model/ModelImporter.kt | 4 +-- .../CqlPreprocessorElmCommonVisitor.kt | 1 + .../cql/elm/requirements/TypeResolver.java | 4 +-- .../java/org/hl7/cql/model/BaseDataType.java | 10 +++---- .../main/java/org/hl7/cql/model/ChoiceType.kt | 16 +++++------ .../main/java/org/hl7/cql/model/DataType.java | 26 ----------------- .../main/java/org/hl7/cql/model/DataType.kt | 28 +++++++++++++++++++ .../tools/xsd2modelinfo/ModelImporter.java | 3 +- 12 files changed, 64 insertions(+), 60 deletions(-) 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 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 e9e62abb2..e06631c0f 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 @@ -3050,7 +3050,7 @@ class Cql2ElmVisitor( mCodeComparator = if ( terminology.resultType.isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! ) ) "in" @@ -3165,12 +3165,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")!! ) ) "~" @@ -3178,7 +3178,7 @@ class Cql2ElmVisitor( } else { if ( terminology.resultType.isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! ) ) "in" @@ -3263,7 +3263,7 @@ class Cql2ElmVisitor( equivalent.operand[1] .resultType .isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! )))) ) { retrieve.codes = libraryBuilder.resolveToList(equivalent.operand[1]) @@ -3285,7 +3285,7 @@ class Cql2ElmVisitor( equal.operand[1] .resultType .isSubTypeOf( - libraryBuilder.resolveTypeName("System", "Vocabulary") + libraryBuilder.resolveTypeName("System", "Vocabulary")!! )))) ) { retrieve.codes = libraryBuilder.resolveToList(equal.operand[1]) @@ -3362,7 +3362,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)) @@ -3771,7 +3771,9 @@ 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")) )) 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 cef1a3f7e..faa3421f9 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 @@ -12,6 +12,7 @@ import org.cqframework.cql.cql2elm.model.invocation.* import org.cqframework.cql.elm.IdObjectFactory import org.cqframework.cql.elm.tracking.Trackable import org.hl7.cql.model.* +import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.cql_annotations.r1.* import org.hl7.cql_annotations.r1.ObjectFactory import org.hl7.elm.r1.* @@ -1021,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) { @@ -1047,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) { @@ -2240,7 +2241,7 @@ class LibraryBuilder( if (compatibleType != null) { return compatibleType } - if (!second.isSubTypeOf(first) && first != null) { + if (first != null && !second.isSubTypeOf(first)) { return ChoiceType(listOf(first, second).flattenChoices()) } 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 5432218bf..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 @@ -98,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 = 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 4d2a9fd57..dcc34c694 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 @@ -6,6 +6,7 @@ import java.util.* import org.cqframework.cql.cql2elm.model.QueryContext import org.cqframework.cql.gen.cqlParser import org.hl7.cql.model.* +import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.elm.r1.* /** Created by Bryn on 12/27/2016. */ @@ -303,7 +304,7 @@ class SystemMethodResolver( 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.toList().flattenChoices())) } 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 818321e2f..5ff960a0d 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 @@ -5,6 +5,7 @@ import kotlin.collections.ArrayList import kotlin.collections.HashMap import org.cqframework.cql.cql2elm.ModelManager import org.hl7.cql.model.ChoiceType +import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.cql.model.ClassType import org.hl7.cql.model.ClassTypeElement import org.hl7.cql.model.DataType @@ -22,7 +23,6 @@ import org.hl7.cql.model.TupleType import org.hl7.cql.model.TupleTypeElement import org.hl7.cql.model.TypeParameter import org.hl7.cql.model.TypeParameter.TypeParameterConstraint -import org.hl7.cql.model.flattenChoices import org.hl7.elm_modelinfo.r1.BoundParameterTypeSpecifier import org.hl7.elm_modelinfo.r1.ChoiceTypeInfo import org.hl7.elm_modelinfo.r1.ChoiceTypeSpecifier @@ -380,7 +380,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { ) result.target = t.target } - resolvedTypes[casify(result!!.name)] = result + resolvedTypes[casify(result.name)] = result } return result 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 a03947fce..3a3ff5a28 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 @@ -22,6 +22,7 @@ import org.cqframework.cql.elm.tracking.Trackable import org.cqframework.cql.gen.cqlBaseVisitor import org.cqframework.cql.gen.cqlParser.* import org.hl7.cql.model.* +import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.cql_annotations.r1.Annotation import org.hl7.cql_annotations.r1.Narrative import org.hl7.cql_annotations.r1.ObjectFactory 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 6906c532c..41c67b402 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 @@ -1,7 +1,5 @@ package org.cqframework.cql.elm.requirements; -import static org.hl7.cql.model.ChoiceTypeKt.flattenChoices; - import java.util.ArrayList; import javax.xml.namespace.QName; import org.cqframework.cql.cql2elm.LibraryManager; @@ -157,7 +155,7 @@ private DataType resolveChoiceTypeSpecifier(ChoiceTypeSpecifier typeSpecifier) { for (TypeSpecifier choiceType : typeSpecifier.getChoice()) { choiceTypes.add(resolveTypeSpecifier(choiceType)); } - return new ChoiceType(flattenChoices(choiceTypes)); + return new ChoiceType(ChoiceType.Companion.flattenChoices(choiceTypes)); } public DataType resolveTypeName(String modelName, String typeName) { diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java b/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java index 68f2efeed..74be5a74f 100644 --- a/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java +++ b/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java @@ -1,15 +1,15 @@ package org.hl7.cql.model; public abstract class BaseDataType implements DataType { - public BaseDataType() { + protected BaseDataType() { this(DataType.ANY); } - public BaseDataType(DataType baseType) { + protected BaseDataType(DataType baseType) { this.baseType = baseType != null ? baseType : DataType.ANY; } - private DataType baseType; + private final DataType baseType; @Override public DataType getBaseType() { @@ -53,14 +53,14 @@ public boolean isSuperTypeOf(DataType other) { @Override public DataType getCommonSuperTypeOf(DataType other) { DataType currentType = this; - while (currentType != null) { + while (currentType != ANY) { if (currentType.isSuperTypeOf(other)) { return currentType; } currentType = currentType.getBaseType(); } - return null; + return ANY; } // Note that this is not how implicit/explicit conversions are defined, the notion of 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 index 8257d7438..ca798fd03 100644 --- 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 @@ -1,5 +1,7 @@ package org.hl7.cql.model +import org.hl7.cql.model.DataType.Companion.ANY + /** Created by Bryn on 11/8/2016. */ data class ChoiceType(val types: Set) : BaseDataType() { init { @@ -71,11 +73,7 @@ data class ChoiceType(val types: Set) : BaseDataType() { return sb.toString() } - override fun isGeneric(): Boolean { - // It hardly makes sense for a choice type to have generics.... - // ignoring in instantiation semantics for now - return types.any { it.isGeneric } - } + 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 @@ -88,7 +86,9 @@ data class ChoiceType(val types: Set) : BaseDataType() { override fun instantiate(context: InstantiationContext): DataType { return this } -} -fun Iterable.flattenChoices(): Set = - flatMap { (it as? ChoiceType)?.types ?: setOf(it) }.toSet() + companion object { + 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/DataType.java b/Src/java/model/src/main/java/org/hl7/cql/model/DataType.java deleted file mode 100644 index c88cf9df5..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/DataType.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.hl7.cql.model; - -public interface DataType { - DataType getBaseType(); - - String toLabel(); - - boolean isSubTypeOf(DataType other); - - boolean isSuperTypeOf(DataType other); - - DataType getCommonSuperTypeOf(DataType other); - - // 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. - boolean isCompatibleWith(DataType other); - - boolean isGeneric(); - - boolean isInstantiable(DataType callType, InstantiationContext context); - - DataType instantiate(InstantiationContext context); - - 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/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 6a6b36c4d..39b6ca058 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 @@ -1,7 +1,6 @@ package org.cqframework.cql.tools.xsd2modelinfo; import static org.cqframework.cql.tools.xsd2modelinfo.ModelImporterOptions.ChoiceTypePolicy.USE_CHOICE; -import static org.hl7.cql.model.ChoiceTypeKt.flattenChoices; import jakarta.xml.bind.JAXB; import java.io.IOException; @@ -637,7 +636,7 @@ private void resolveClassTypeElements(XmlSchemaParticle particle, List Date: Tue, 10 Dec 2024 12:10:38 -0500 Subject: [PATCH 15/27] BaseDataType --- .../java/org/hl7/cql/model/BaseDataType.java | 82 ------------------- .../java/org/hl7/cql/model/BaseDataType.kt | 62 ++++++++++++++ 2 files changed, 62 insertions(+), 82 deletions(-) delete mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.kt diff --git a/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java b/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java deleted file mode 100644 index 74be5a74f..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/BaseDataType.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.hl7.cql.model; - -public abstract class BaseDataType implements DataType { - protected BaseDataType() { - this(DataType.ANY); - } - - protected BaseDataType(DataType baseType) { - this.baseType = baseType != null ? baseType : DataType.ANY; - } - - private final DataType baseType; - - @Override - public DataType getBaseType() { - return baseType; - } - - @Override - public String toLabel() { - return toString(); - } - - @Override - public boolean isSubTypeOf(DataType other) { - DataType currentType = this; - while (currentType != null) { - if (currentType.equals(other)) { - return true; - } - currentType = currentType.getBaseType(); - } - - return false; - } - - @Override - public boolean isSuperTypeOf(DataType other) { - while (other != null) { - if (equals(other)) { - return true; - } - other = other.getBaseType(); - } - - return false; - } - - /** - * @param other - * @return The first supertype of this type that is also a supertype of other - */ - @Override - public DataType getCommonSuperTypeOf(DataType other) { - DataType currentType = this; - while (currentType != ANY) { - if (currentType.isSuperTypeOf(other)) { - return currentType; - } - currentType = currentType.getBaseType(); - } - - return 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 - 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 - } -} 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 + } + } +} From 7e561d0babc4f3da692f726747cfdffef3aa2f7d Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 12:24:08 -0500 Subject: [PATCH 16/27] Little bit of cleanup for choice types --- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 9 ++------- .../cql/cql2elm/SystemMethodResolver.kt | 3 +-- .../cql/cql2elm/model/ModelImporter.kt | 5 ++--- .../CqlPreprocessorElmCommonVisitor.kt | 3 +-- .../cql/elm/requirements/TypeResolver.java | 2 +- .../main/java/org/hl7/cql/model/ChoiceType.kt | 19 ++++++------------- .../tools/xsd2modelinfo/ModelImporter.java | 2 +- 7 files changed, 14 insertions(+), 29 deletions(-) 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 faa3421f9..743374803 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 @@ -12,7 +12,6 @@ import org.cqframework.cql.cql2elm.model.invocation.* import org.cqframework.cql.elm.IdObjectFactory import org.cqframework.cql.elm.tracking.Trackable import org.hl7.cql.model.* -import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.cql_annotations.r1.* import org.hl7.cql_annotations.r1.ObjectFactory import org.hl7.elm.r1.* @@ -2242,7 +2241,7 @@ class LibraryBuilder( return compatibleType } if (first != null && !second.isSubTypeOf(first)) { - return ChoiceType(listOf(first, second).flattenChoices()) + return ChoiceType(first, second) } // The above construction of a choice type guarantees this will never be hit @@ -2541,11 +2540,7 @@ class LibraryBuilder( // The result type is a choice of all the resolved types if (resultTypes.size > 1) { - return PropertyResolution( - ChoiceType(resultTypes.flattenChoices()), - name!!, - resultTargetMaps - ) + return PropertyResolution(ChoiceType(resultTypes), name!!, resultTargetMaps) } if (resultTypes.size == 1) { return PropertyResolution( 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 dcc34c694..cbdfc9c97 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 @@ -6,7 +6,6 @@ import java.util.* import org.cqframework.cql.cql2elm.model.QueryContext import org.cqframework.cql.gen.cqlParser import org.hl7.cql.model.* -import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.elm.r1.* /** Created by Bryn on 12/27/2016. */ @@ -306,7 +305,7 @@ class SystemMethodResolver( if (dataTypes.size == 1) { children.resultType = ListType(dataTypes.iterator().next()) } else { - children.resultType = ListType(ChoiceType(dataTypes.toList().flattenChoices())) + children.resultType = ListType(ChoiceType(dataTypes)) } children } 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 5ff960a0d..49b5ff1ba 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 @@ -5,7 +5,6 @@ import kotlin.collections.ArrayList import kotlin.collections.HashMap import org.cqframework.cql.cql2elm.ModelManager import org.hl7.cql.model.ChoiceType -import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.cql.model.ClassType import org.hl7.cql.model.ClassTypeElement import org.hl7.cql.model.DataType @@ -245,7 +244,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { val choiceType = resolveTypeSpecifier(choice)!! choices.add(choiceType) } - return ChoiceType(choices.flattenChoices()) + return ChoiceType(choices) } return null @@ -708,7 +707,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { types.add(resolveTypeSpecifier(typeSpecifier)!!) } } - return ChoiceType(types.flattenChoices()) + return ChoiceType(types) } /** 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 3a3ff5a28..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 @@ -22,7 +22,6 @@ import org.cqframework.cql.elm.tracking.Trackable import org.cqframework.cql.gen.cqlBaseVisitor import org.cqframework.cql.gen.cqlParser.* import org.hl7.cql.model.* -import org.hl7.cql.model.ChoiceType.Companion.flattenChoices import org.hl7.cql_annotations.r1.Annotation import org.hl7.cql_annotations.r1.Narrative import org.hl7.cql_annotations.r1.ObjectFactory @@ -176,7 +175,7 @@ abstract class CqlPreprocessorElmCommonVisitor( if (includeDeprecatedElements) { result.type.addAll(typeSpecifiers) } - val choiceType = ChoiceType(types.flattenChoices()) + val choiceType = ChoiceType(types) result.resultType = choiceType return result } 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 41c67b402..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 @@ -155,7 +155,7 @@ private DataType resolveChoiceTypeSpecifier(ChoiceTypeSpecifier typeSpecifier) { for (TypeSpecifier choiceType : typeSpecifier.getChoice()) { choiceTypes.add(resolveTypeSpecifier(choiceType)); } - return new ChoiceType(ChoiceType.Companion.flattenChoices(choiceTypes)); + return new ChoiceType(choiceTypes); } public DataType resolveTypeName(String modelName, String typeName) { 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 index ca798fd03..9e43dbc0c 100644 --- 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 @@ -3,7 +3,11 @@ package org.hl7.cql.model import org.hl7.cql.model.DataType.Companion.ANY /** Created by Bryn on 11/8/2016. */ -data class ChoiceType(val types: Set) : BaseDataType() { +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 }) { @@ -11,13 +15,6 @@ data class ChoiceType(val types: Set) : BaseDataType() { } } - override fun isSubTypeOf(other: DataType): Boolean { - // Choice types do not follow the is-a relationship, they use the is-compatible relationship - // instead, defined - // using subset/superset - return super.isSubTypeOf(other) - } - fun isSubSetOf(other: ChoiceType): Boolean { for (type in types) { var currentIsSubType = false @@ -36,10 +33,6 @@ data class ChoiceType(val types: Set) : BaseDataType() { return true } - override fun isSuperTypeOf(other: DataType): Boolean { - return super.isSuperTypeOf(other) - } - fun isSuperSetOf(other: ChoiceType): Boolean { return other.isSubSetOf(this) } @@ -88,7 +81,7 @@ data class ChoiceType(val types: Set) : BaseDataType() { } companion object { - fun Iterable.flattenChoices(): Set = + private fun Iterable.flattenChoices(): Set = flatMap { (it as? ChoiceType)?.types ?: setOf(it) }.toSet() } } 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 39b6ca058..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 @@ -636,7 +636,7 @@ private void resolveClassTypeElements(XmlSchemaParticle particle, List Date: Tue, 10 Dec 2024 14:39:54 -0500 Subject: [PATCH 17/27] Fixing Locale for string format --- ...otlin-multiplatform-conventions.gradle.kts | 3 + .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 137 ++++++++++++++---- .../cqframework/cql/cql2elm/CqlCompiler.kt | 3 +- .../cql/cql2elm/CqlTranslatorOptionsMapper.kt | 25 +++- .../org/cqframework/cql/cql2elm/DataTypes.kt | 5 +- .../cql/cql2elm/DefaultLibrarySourceLoader.kt | 2 +- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 72 +++++++-- .../cqframework/cql/cql2elm/LibraryManager.kt | 10 +- .../cql/cql2elm/LibraryReaderUtil.kt | 8 +- .../cql/cql2elm/ModelInfoLoader.kt | 2 +- .../cqframework/cql/cql2elm/ModelManager.kt | 3 +- .../cql2elm/StringLibrarySourceProvider.kt | 4 +- .../cql/cql2elm/SystemMethodResolver.kt | 2 +- .../cqframework/cql/cql2elm/TypeBuilder.kt | 5 +- .../main/java/org/hl7/cql/model/ChoiceType.kt | 1 + 15 files changed, 211 insertions(+), 71 deletions(-) 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/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 e06631c0f..eaa4fbb06 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", @@ -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 @@ -227,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) { @@ -258,6 +262,7 @@ class Cql2ElmVisitor( else -> throw IllegalArgumentException( String.format( + Locale.US, "Unknown access modifier %s.", ctx.text.lowercase(Locale.getDefault()) ) @@ -294,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) @@ -315,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() @@ -337,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 ) @@ -393,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()) { @@ -404,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 @@ -570,6 +577,7 @@ class Cql2ElmVisitor( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Identifier %s is already in use in this library.", expressionDef.name ) @@ -774,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) @@ -782,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) @@ -791,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) @@ -800,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) @@ -810,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 ), @@ -819,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 ) @@ -898,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) @@ -916,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) @@ -925,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) @@ -934,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) @@ -943,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) @@ -952,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) @@ -967,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 ) @@ -980,6 +1019,7 @@ class Cql2ElmVisitor( ) { throw IllegalArgumentException( String.format( + Locale.US, "Timezone minute offset is out of range in date/time literal (%s).", input ) @@ -995,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 ) @@ -1025,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 ), @@ -1034,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 ) @@ -1084,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) ) } } @@ -1151,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( @@ -1200,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) { @@ -1268,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) + ) } } @@ -1336,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) ) } } @@ -1382,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 @@ -1573,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? { @@ -1684,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))) @@ -1742,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 ) @@ -1792,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 @@ -1869,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) { @@ -1971,6 +2021,7 @@ class Cql2ElmVisitor( else -> throw IllegalArgumentException( String.format( + Locale.US, "Unknown relative qualifier: '%s'.", ctx.relativeQualifier()!!.text ) @@ -2795,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) ) } @@ -2851,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) ) } @@ -2868,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 @@ -2933,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 @@ -3216,6 +3272,7 @@ class Cql2ElmVisitor( libraryBuilder.recordParsingException( CqlSemanticException( String.format( + Locale.US, "Unexpected membership operator %s in retrieve", inExpression.javaClass.simpleName ), @@ -3298,6 +3355,7 @@ class Cql2ElmVisitor( libraryBuilder.recordParsingException( CqlSemanticException( String.format( + Locale.US, "Unknown code comparator %s in retrieve", codeComparator ), @@ -3646,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) } } } @@ -4092,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 ) @@ -4258,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 ), @@ -4265,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) @@ -4364,6 +4424,7 @@ class Cql2ElmVisitor( } else { throw IllegalArgumentException( String.format( + Locale.US, "Internal error attempting to resolve function header for %s", op.name ) @@ -4392,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 + ) ) } @@ -4404,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 ) @@ -4425,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 ) @@ -4461,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, @@ -4477,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 ) 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 2720195c2..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 @@ -77,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/LibraryBuilder.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/LibraryBuilder.kt index 743374803..55818d905 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 @@ -475,7 +474,9 @@ class LibraryBuilder( // Special case for FHIR-derived models that include FHIR Helpers return modelManager.resolveModelByUri("http://hl7.org/fhir") } - requireNotNull(usingDef) { String.format("Could not resolve model name %s", modelName) } + requireNotNull(usingDef) { + String.format(Locale.US, "Could not resolve model name %s", modelName) + } return getModel(usingDef) } @@ -511,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? { 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 } @@ -566,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()) ) } } @@ -1370,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 @@ -1483,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 @@ -1562,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 @@ -1570,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 @@ -1579,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 @@ -1599,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 @@ -2467,6 +2474,7 @@ class LibraryBuilder( 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 @@ -2492,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) { @@ -2512,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, @@ -2528,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, @@ -2565,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 @@ -2652,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 ) @@ -2668,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 ) @@ -2684,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 ) @@ -2703,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 ) @@ -2718,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 ) @@ -2733,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 ) @@ -2772,6 +2797,7 @@ class LibraryBuilder( if (message == null) { message = String.format( + Locale.US, "Could not resolve identifier %s in the current library.", identifier ) @@ -2808,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 ) @@ -2863,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, @@ -2957,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) @@ -3068,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 @@ -3083,6 +3116,7 @@ class LibraryBuilder( } else { throw IllegalArgumentException( String.format( + Locale.US, "Cannot resolve %%parent reference in targetMap %s", targetMap ) @@ -3107,6 +3141,7 @@ class LibraryBuilder( if (indexerItems.size != 2) { throw IllegalArgumentException( String.format( + Locale.US, "Invalid indexer item %s in targetMap %s", indexerItem, targetMap @@ -3269,7 +3304,7 @@ class LibraryBuilder( } } throw IllegalArgumentException( - String.format("TargetMapping not implemented: %s", targetMap) + String.format(Locale.US, "TargetMapping not implemented: %s", targetMap) ) } @@ -3367,6 +3402,7 @@ class LibraryBuilder( // ERROR: throw IllegalArgumentException( String.format( + Locale.US, "Could not resolve identifier %s in library %s.", memberIdentifier, referencedLibrary.identifier!!.id @@ -3552,6 +3588,7 @@ class LibraryBuilder( } throw IllegalArgumentException( String.format( + Locale.US, "Invalid context reference from %s context to %s context.", currentExpressionContext(), expressionDef.context @@ -3684,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 @@ -3739,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 966068c5e..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( @@ -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 @@ -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/ModelInfoLoader.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/ModelInfoLoader.kt index 2906c9c0f..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 @@ -36,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), 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 fb2a7d36a..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 @@ -275,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/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/SystemMethodResolver.kt b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/SystemMethodResolver.kt index cbdfc9c97..2002e8cdf 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 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/model/src/main/java/org/hl7/cql/model/ChoiceType.kt b/Src/java/model/src/main/java/org/hl7/cql/model/ChoiceType.kt index 9e43dbc0c..5fe04962b 100644 --- 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 @@ -2,6 +2,7 @@ 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()) From c7e55de4dd43858111c3031fbaed56fda488aafa Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 16:11:46 -0500 Subject: [PATCH 18/27] ClassType --- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 2 +- .../cqframework/cql/cql2elm/model/Model.kt | 2 +- .../cql/cql2elm/model/ModelImporter.kt | 5 +- .../main/java/org/hl7/cql/model/ChoiceType.kt | 31 +- .../java/org/hl7/cql/model/ClassType.java | 473 ------------------ .../main/java/org/hl7/cql/model/ClassType.kt | 302 +++++++++++ .../model/GenericClassSignatureParser.java | 3 - .../java/org/hl7/cql/model/ProfileType.java | 17 +- .../GenericClassSignatureParserTest.java | 28 +- 9 files changed, 328 insertions(+), 535 deletions(-) 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 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 55818d905..113e3363e 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 @@ -2463,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) } 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 f7cb30678..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) { 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 49b5ff1ba..4da2bca96 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 @@ -766,9 +766,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 } /** 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 index 5fe04962b..bda8b1bd0 100644 --- 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 @@ -17,21 +17,8 @@ data class ChoiceType private constructor(val types: Set) : BaseDataTy } fun isSubSetOf(other: ChoiceType): Boolean { - for (type in types) { - var currentIsSubType = false - for (otherType in other.types) { - currentIsSubType = type.isSubTypeOf(otherType) - if (currentIsSubType) { - break - } - } - - if (!currentIsSubType) { - return false - } - } - - return true + // 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 { @@ -52,19 +39,7 @@ data class ChoiceType private constructor(val types: Set) : BaseDataTy } override fun toString(): String { - val sb = StringBuilder() - sb.append("choice<") - var first = true - for (type in types) { - if (first) { - first = false - } else { - sb.append(",") - } - sb.append(type.toString()) - } - sb.append(">") - return sb.toString() + return types.joinToString(",", "choice<", ">") } override val isGeneric: Boolean = types.any { it.isGeneric } 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 f562b8e02..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 BaseDataType implements NamedType { - - public ClassType( - String name, - DataType baseType, - Collection elements, - Collection parameters) { - super(baseType); - - if (name == null || name.isEmpty()) { - 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.getName() != null) { - int qualifierIndex = this.getName() - .indexOf('.'); // TODO Should this not be the last occurrence rather than the first occurrence? - if (qualifierIndex > 0) { - return this.getName().substring(0, qualifierIndex); - } - } - - return ""; - } - - public String getSimpleName() { - if (this.getName() != null) { - int qualifierIndex = this.getName() - .indexOf('.'); // TODO Should this not be the last occurrence rather than the first occurrence? - if (qualifierIndex > 0) { - return this.getName().substring(qualifierIndex + 1); - } - } - - return this.getName(); - } - - 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.getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof ClassType) { - ClassType that = (ClassType) o; - return this.getName().equals(that.getName()); - } - - return false; - } - - @Override - public String toString() { - return this.getName(); - } - - @Override - public String toLabel() { - return this.label == null ? this.getName() : 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.getProhibited()) { - TupleTypeElement tupleElement = new TupleTypeElement(element.getName(), element.getType(), false); - 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), false, false, null)); - } - - 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..9fc24a0d9 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ClassType.kt @@ -0,0 +1,302 @@ +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 { + return this.name + } + + override fun toLabel(): String { + return if (this.label == null) this.name else label!! + } + + 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) + } + + 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/GenericClassSignatureParser.java b/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.java index 173ec241a..8ab2702e6 100644 --- 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 @@ -187,9 +187,6 @@ protected DataType handleBoundType(String boundGenericSignature) { } 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('>')); 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 index 10e1e61ff..5862c9d78 100644 --- 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 @@ -1,6 +1,7 @@ package org.hl7.cql.model; -import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * Created by Bryn on 8/22/2016. @@ -24,19 +25,11 @@ * 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, List elements) { + super(name, baseType, elements, Collections.emptyList()); } public ProfileType(String name, DataType baseType) { - super(name, baseType); + this(name, baseType, Collections.emptyList()); } } 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 db0c90489..b466a504d 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 @@ -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,8 +96,8 @@ 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"), false, false, null)); Map resolvedTypes = new HashMap<>(); @@ -125,12 +125,12 @@ 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"), false, false, null)); - ClassType mapType = new ClassType("Map", null, 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"), false, false, null)); @@ -162,12 +162,12 @@ 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"), false, false, null)); - ClassType mapType = new ClassType("Map", null, 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"), false, false, null)); From 23ce817967767388ca5e5c0ba9d507dfa87d5aee Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 16:15:09 -0500 Subject: [PATCH 19/27] ProfileType --- .../java/org/hl7/cql/model/ProfileType.java | 35 ------------------- .../java/org/hl7/cql/model/ProfileType.kt | 31 ++++++++++++++++ 2 files changed, 31 insertions(+), 35 deletions(-) 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 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 5862c9d78..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/ProfileType.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.hl7.cql.model; - -import java.util.Collections; -import java.util.List; - -/** - * 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, List elements) { - super(name, baseType, elements, Collections.emptyList()); - } - - public ProfileType(String name, DataType baseType) { - this(name, baseType, Collections.emptyList()); - } -} 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) From aff5257ea39727b281533b176812e28e04f7f5d3 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 16:34:46 -0500 Subject: [PATCH 20/27] ListType --- .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 14 +-- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 2 +- .../cql/cql2elm/SystemMethodResolver.kt | 2 +- .../cql/cql2elm/model/ModelImporter.kt | 4 +- .../main/java/org/hl7/cql/model/ListType.java | 105 ------------------ .../main/java/org/hl7/cql/model/ListType.kt | 60 ++++++++++ 6 files changed, 71 insertions(+), 116 deletions(-) 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 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 eaa4fbb06..ee4e129ea 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 @@ -280,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) @@ -353,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) @@ -759,7 +759,7 @@ class Cql2ElmVisitor( } } if (listType == null) { - listType = ListType(elementType) + listType = ListType(elementType!!) } list.resultType = listType return list @@ -3047,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 @@ -3065,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") @@ -3146,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 @@ -3179,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 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 113e3363e..f7386d828 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 @@ -449,7 +449,7 @@ class LibraryBuilder( typeSpecifier.lastIndexOf('>') ) ) - ListType(elementType) + ListType(elementType!!) } else if (typeSpecifier.indexOf(".") >= 0) { val modelName = typeSpecifier.substring(0, typeSpecifier.indexOf(".")) val typeName = typeSpecifier.substring(typeSpecifier.indexOf(".") + 1) 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 2002e8cdf..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 @@ -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)) 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 4da2bca96..c4ee8b26a 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 @@ -268,7 +268,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { resolveTypeName( typeName.substring(typeName.indexOf('<') + 1, typeName.lastIndexOf('>')) ) - return ListType(elementType) + return ListType(elementType!!) } var result = lookupType(typeName) @@ -692,7 +692,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { } private fun resolveListType(t: ListTypeInfo): ListType { - val result = ListType(resolveTypeNameOrSpecifier(t.elementType, t.elementTypeSpecifier)) + val result = ListType(resolveTypeNameOrSpecifier(t.elementType, t.elementTypeSpecifier)!!) return result } 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 bcadfad40..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 BaseDataType { - 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..e96a6f808 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/ListType.kt @@ -0,0 +1,60 @@ +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 + get() = 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)) + } +} From 31526a158f6f8aa4f1b86802441b62ad6b07a321 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 16:51:22 -0500 Subject: [PATCH 21/27] IntervalType --- .../cqframework/cql/cql2elm/Cql2ElmVisitor.kt | 2 +- .../cqframework/cql/cql2elm/LibraryBuilder.kt | 4 +- .../cql/cql2elm/model/ModelImporter.kt | 6 +- .../java/org/hl7/cql/model/IntervalType.java | 105 ------------------ .../java/org/hl7/cql/model/IntervalType.kt | 60 ++++++++++ 5 files changed, 66 insertions(+), 111 deletions(-) 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 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 ee4e129ea..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 @@ -3833,7 +3833,7 @@ class Cql2ElmVisitor( 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 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 f7386d828..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 @@ -438,7 +438,7 @@ class LibraryBuilder( typeSpecifier.lastIndexOf('>') ) ) - return IntervalType(pointType) + return IntervalType(pointType!!) } else -> return if (typeSpecifier.lowercase(Locale.getDefault()).startsWith("list<")) { @@ -2411,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 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 c4ee8b26a..1ce93c8c5 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 @@ -212,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) { @@ -262,7 +262,7 @@ 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( @@ -687,7 +687,7 @@ 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 } 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 c2faed546..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 BaseDataType { - 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)) + } +} From dcf26cde552bb6961810ef72e48a9c684570e617 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 17:00:24 -0500 Subject: [PATCH 22/27] TypeParameter --- .../cql/cql2elm/model/ModelImporter.kt | 48 ++++--- .../java/org/hl7/cql/model/TypeParameter.java | 131 ------------------ .../java/org/hl7/cql/model/TypeParameter.kt | 71 ++++++++++ 3 files changed, 96 insertions(+), 154 deletions(-) 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 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 1ce93c8c5..a107fe8bf 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 @@ -431,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, 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 3226760ab..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 BaseDataType { - 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..2bb89e958 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.kt @@ -0,0 +1,71 @@ +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 { + return identifier + } + + override val isGeneric: Boolean + get() = true + + override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { + return context.isInstantiable(this, callType) + } + + override fun instantiate(context: InstantiationContext): DataType { + return context.instantiate(this) + } +} From 4cbfae979068419297e20a428fee990383f6d783 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 20:49:35 -0500 Subject: [PATCH 23/27] TupleType, ClassType --- .../cql/cql2elm/model/ModelImporter.kt | 6 +- .../main/java/org/hl7/cql/model/ClassType.kt | 16 +- .../main/java/org/hl7/cql/model/ListType.kt | 3 +- .../java/org/hl7/cql/model/SimpleType.java | 112 --------- .../main/java/org/hl7/cql/model/SimpleType.kt | 64 ++++++ .../java/org/hl7/cql/model/TupleType.java | 216 ------------------ .../main/java/org/hl7/cql/model/TupleType.kt | 91 ++++++++ .../java/org/hl7/cql/model/TypeParameter.kt | 7 +- 8 files changed, 166 insertions(+), 349 deletions(-) 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/TupleType.java create mode 100644 Src/java/model/src/main/java/org/hl7/cql/model/TupleType.kt 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 a107fe8bf..8095826b6 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 @@ -375,9 +375,9 @@ 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 } @@ -405,7 +405,7 @@ class ModelImporter(val modelInfo: ModelInfo, val modelManager: ModelManager?) { } private fun resolveTupleType(t: TupleTypeInfo): TupleType { - val result = TupleType(resolveTupleTypeElements(t.element)) + val result = TupleType(resolveTupleTypeElements(t.element).toMutableList()) 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 index 9fc24a0d9..565f3877c 100644 --- 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 @@ -226,16 +226,11 @@ constructor( return false } - override fun toString(): String { - return this.name - } + override fun toString(): String = name - override fun toLabel(): String { - return if (this.label == null) this.name else label!! - } + override fun toLabel(): String = label ?: name - val tupleType: TupleType - get() = buildTupleType() + val tupleType: TupleType = buildTupleType() private fun addTupleElements( classType: ClassType, @@ -260,7 +255,7 @@ constructor( addTupleElements(this, tupleElements) - return TupleType(tupleElements.values) + return TupleType(tupleElements.values.toMutableList()) } override fun isCompatibleWith(other: DataType): Boolean { @@ -277,8 +272,7 @@ constructor( return super.isCompatibleWith(other) } - override val isGeneric: Boolean - get() = genericParameters.isNotEmpty() + override val isGeneric: Boolean = genericParameters.isNotEmpty() override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { return if (callType is ClassType && callType.elements.size == elements.size) { 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 index e96a6f808..942a01299 100644 --- 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 @@ -28,8 +28,7 @@ data class ListType(val elementType: DataType) : BaseDataType() { return String.format(Locale.US, "List of %s", elementType.toLabel()) } - override val isGeneric: Boolean - get() = elementType.isGeneric + override val isGeneric: Boolean = elementType.isGeneric override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { return when (callType) { 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 9f73efc6b..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 BaseDataType 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/TupleType.java b/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.java deleted file mode 100644 index b9131f7a5..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 BaseDataType { - 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); - sortedElements.sort((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), false)); - } - - 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..7bf0f48ae --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/TupleType.kt @@ -0,0 +1,91 @@ +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 = + 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 = 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/TypeParameter.kt b/Src/java/model/src/main/java/org/hl7/cql/model/TypeParameter.kt index 2bb89e958..baad6bbd9 100644 --- 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 @@ -54,12 +54,9 @@ constructor( } } - override fun toString(): String { - return identifier - } + override fun toString(): String = identifier - override val isGeneric: Boolean - get() = true + override val isGeneric: Boolean = true override fun isInstantiable(callType: DataType, context: InstantiationContext): Boolean { return context.isInstantiable(this, callType) From dbf3c7bad9a6d8915a9dbd7ab813bbda6e5e8af7 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 21:21:32 -0500 Subject: [PATCH 24/27] Bug fixes for classes and tuples --- .../model/src/main/java/org/hl7/cql/model/ClassType.kt | 6 ++++-- .../model/src/main/java/org/hl7/cql/model/TupleType.kt | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) 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 index 565f3877c..4e818c3d5 100644 --- 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 @@ -230,7 +230,8 @@ constructor( override fun toLabel(): String = label ?: name - val tupleType: TupleType = buildTupleType() + val tupleType: TupleType + get() = buildTupleType() private fun addTupleElements( classType: ClassType, @@ -272,7 +273,8 @@ constructor( return super.isCompatibleWith(other) } - override val isGeneric: Boolean = genericParameters.isNotEmpty() + 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) { 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 index 7bf0f48ae..0802205c8 100644 --- 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 @@ -13,8 +13,8 @@ constructor(val elements: MutableList = mutableListOf()) : Bas this.elements.addAll(elements) } - val sortedElements: List = - elements.sortedWith { l, r -> l.name.compareTo(r.name) } + val sortedElements: List + get() = elements.sortedWith { l, r -> l.name.compareTo(r.name) } override fun isSubTypeOf(other: DataType): Boolean { return when { @@ -46,7 +46,8 @@ constructor(val elements: MutableList = mutableListOf()) : Bas } else super.isCompatibleWith(other) } - override val isGeneric: Boolean = elements.any { it.type.isGeneric } + 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 From d3643391b9d9cdf61784c6c290f155da2065dfd4 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 21:34:20 -0500 Subject: [PATCH 25/27] GenericClassSignatureParser --- .../model/GenericClassSignatureParser.java | 392 ------------------ .../cql/model/GenericClassSignatureParser.kt | 346 ++++++++++++++++ .../GenericClassSignatureParserTest.java | 2 +- 3 files changed, 347 insertions(+), 393 deletions(-) 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 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 8ab2702e6..000000000 --- a/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.java +++ /dev/null @@ -1,392 +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, false, false, null); - 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); - 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, false, false, null); - 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..eaee032f9 --- /dev/null +++ b/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.kt @@ -0,0 +1,346 @@ +package org.hl7.cql.model + +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>. + */ +@Suppress("MagicNumber", "TooGenericExceptionThrown", "TooManyFunctions", "NestedBlockDepth") +class GenericClassSignatureParser( + /** A generic signature such as List<T> or a bound signature such as List<Person> */ + var genericSignature: String, + /** The base type for the class type or the profile. */ + var baseType: String?, + /** The name of a bound type such as PersonList = List<Person> */ + var boundGenericTypeName: String?, + private val resolvedTypes: MutableMap +) { + @JvmOverloads + constructor( + genericSignature: String, + resolvedTypes: MutableMap = HashMap() + ) : this(genericSignature, null, 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: Array? = 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() } + .toTypedArray() + } + val baseDataType = resolveTypeName(baseTypeName) + val genericClassType = ClassType(genericTypeName, baseDataType) + for (param in params) { + val paramType = handleParameterDeclaration(unescapeNestedCommas(param)) + genericClassType.addGenericParameter(paramType) + } + if (baseTypeParameters != null) { + var index = 0 + 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 + val myParam = baseTypeClass.genericParameters[index].identifier + println(boundType.toString() + " replaces param " + myParam) + for ((name) in baseClassFields) { + val myElement = ClassTypeElement(name, boundType!!, false, false, null) + 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. + */ + private fun handleParameterDeclaration(parameterString: String): TypeParameter { + val paramComponents = + parameterString.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + return if (paramComponents.size == 1) { + TypeParameter( + StringUtils.trim(parameterString), + 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() } + .toTypedArray() + var index = 0 + for (param in params) { + var param = param + var boundParam: DataType? = null + param = unescapeNestedCommas(param) + boundParam = + if (isValidGenericSignature(param)) { + handleBoundType(param) + } else { + resolveType(param) + } + val typeParameter = resolvedType.genericParameters[index] + for ((name, type) in resolvedType.elements) { + if (type is TypeParameter) { + if ( + (type as TypeParameter) + .identifier + .equals(typeParameter.identifier, ignoreCase = true) + ) { + val newElement = ClassTypeElement(name, boundParam, false, false, null) + newType.addElement(newElement) + } + } + } + index++ + } + 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 + */ + fun isValidGenericSignature(genericSignature: String?): Boolean { + return areBracketsPaired(genericSignature) && + closingBracketsComeAfterOpeningBrackets(genericSignature) + } + + /** + * Counts the number of < in this signature. + * + * @param signatureString + * @return + */ + /** + * Counts the number of < in this signature. + * + * @return + */ + private fun openBracketCount(signatureString: String? = genericSignature): Int { + var matchCount = 0 + if (signatureString != null) { + matchCount = StringUtils.countMatches(signatureString, OPEN_BRACKET) + } + return matchCount + } + + /** + * Counts the number of > in this signature. + * + * @param signatureString + * @return + */ + /** + * Counts the number of > in this signature. + * + * @return + */ + private fun closeBracketCount(signatureString: String? = genericSignature): Int { + var matchCount = 0 + if (signatureString != null) { + matchCount = StringUtils.countMatches(signatureString, CLOSE_BRACKET) + } + return matchCount + } + + /** + * Method returns if the number of < matches the number of > + * + * @param signatureString + * @return + */ + /** + * 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. + * + * @param signatureString + * @return + */ + /** + * 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/test/java/org/hl7/cql/model/GenericClassSignatureParserTest.java b/Src/java/model/src/test/java/org/hl7/cql/model/GenericClassSignatureParserTest.java index b466a504d..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")); From 0fba7b3a1427aab2613f5f8e082809127066ee3b Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Tue, 10 Dec 2024 21:55:13 -0500 Subject: [PATCH 26/27] More cleanup of GenericClassSignatureParser --- .../cql/cql2elm/model/ModelImporter.kt | 2 +- Src/java/model/build.gradle.kts | 4 - .../cql/model/GenericClassSignatureParser.kt | 119 ++++++------------ 3 files changed, 40 insertions(+), 85 deletions(-) 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 8095826b6..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 @@ -781,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/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/GenericClassSignatureParser.kt b/Src/java/model/src/main/java/org/hl7/cql/model/GenericClassSignatureParser.kt index eaee032f9..f3d83b34c 100644 --- 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 @@ -1,7 +1,5 @@ package org.hl7.cql.model -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 @@ -12,18 +10,16 @@ import org.apache.commons.lang3.StringUtils @Suppress("MagicNumber", "TooGenericExceptionThrown", "TooManyFunctions", "NestedBlockDepth") class GenericClassSignatureParser( /** A generic signature such as List<T> or a bound signature such as List<Person> */ - var genericSignature: String, + private var genericSignature: String, /** The base type for the class type or the profile. */ var baseType: String?, - /** The name of a bound type such as PersonList = List<Person> */ - var boundGenericTypeName: String?, private val resolvedTypes: MutableMap ) { @JvmOverloads constructor( genericSignature: String, resolvedTypes: MutableMap = HashMap() - ) : this(genericSignature, null, null, resolvedTypes) + ) : this(genericSignature, null, resolvedTypes) /** * Parses a generic type declaration such as Map<K,V>. @@ -44,16 +40,15 @@ class GenericClassSignatureParser( escapeNestedCommas(parameters).split(",".toRegex()).dropLastWhile { it.isEmpty() } } var baseTypeName = baseType - var baseTypeParameters: Array? = null + 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() } - .toTypedArray() + escapeNestedCommas(baseTypeParameterString).split(",".toRegex()).dropLastWhile { + it.isEmpty() + } } val baseDataType = resolveTypeName(baseTypeName) val genericClassType = ClassType(genericTypeName, baseDataType) @@ -62,7 +57,6 @@ class GenericClassSignatureParser( genericClassType.addGenericParameter(paramType) } if (baseTypeParameters != null) { - var index = 0 for (baseTypeParameter in baseTypeParameters) { if ( baseTypeParameter.length == 1 && @@ -73,14 +67,18 @@ class GenericClassSignatureParser( val boundType = resolveTypeName(unescapeNestedCommas(baseTypeParameter)) val baseTypeClass = baseDataType as ClassType? val baseClassFields: List = baseTypeClass!!.elements - val myParam = baseTypeClass.genericParameters[index].identifier - println(boundType.toString() + " replaces param " + myParam) for ((name) in baseClassFields) { - val myElement = ClassTypeElement(name, boundType!!, false, false, null) + val myElement = + ClassTypeElement( + name, + boundType!!, + prohibited = false, + oneBased = false, + target = null + ) genericClassType.addElement(myElement) } } - index++ } } return genericClassType @@ -93,14 +91,9 @@ class GenericClassSignatureParser( * @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() }.toTypedArray() + val paramComponents = parameterString.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() } return if (paramComponents.size == 1) { - TypeParameter( - StringUtils.trim(parameterString), - TypeParameter.TypeParameterConstraint.NONE, - null - ) + TypeParameter(parameterString.trim(), TypeParameter.TypeParameterConstraint.NONE, null) } else if (paramComponents.size == 3) { if (paramComponents[1].equals(EXTENDS, ignoreCase = true)) { TypeParameter( @@ -158,35 +151,33 @@ class GenericClassSignatureParser( boundGenericSignature.lastIndexOf('>') ) val params = - escapeNestedCommas(parameters) - .split(",".toRegex()) - .dropLastWhile { it.isEmpty() } - .toTypedArray() - var index = 0 - for (param in params) { - var param = param - var boundParam: DataType? = null - param = unescapeNestedCommas(param) + escapeNestedCommas(parameters).split(",".toRegex()).dropLastWhile { it.isEmpty() } + for ((index, param) in params.withIndex()) { + var boundParam: DataType? + val unescaped = unescapeNestedCommas(param) boundParam = - if (isValidGenericSignature(param)) { - handleBoundType(param) + if (isValidGenericSignature(unescaped)) { + handleBoundType(unescaped) } else { - resolveType(param) + resolveType(unescaped) } val typeParameter = resolvedType.genericParameters[index] for ((name, type) in resolvedType.elements) { - if (type is TypeParameter) { - if ( - (type as TypeParameter) - .identifier - .equals(typeParameter.identifier, ignoreCase = true) - ) { - val newElement = ClassTypeElement(name, boundParam, false, false, null) - newType.addElement(newElement) - } + 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) } } - index++ } resolvedTypes[newType.name] = newType return newType @@ -207,55 +198,29 @@ class GenericClassSignatureParser( * @param genericSignature * @return True if the generic signature is valid */ - fun isValidGenericSignature(genericSignature: String?): Boolean { + private fun isValidGenericSignature(genericSignature: String?): Boolean { return areBracketsPaired(genericSignature) && closingBracketsComeAfterOpeningBrackets(genericSignature) } - /** - * Counts the number of < in this signature. - * - * @param signatureString - * @return - */ /** * Counts the number of < in this signature. * * @return */ private fun openBracketCount(signatureString: String? = genericSignature): Int { - var matchCount = 0 - if (signatureString != null) { - matchCount = StringUtils.countMatches(signatureString, OPEN_BRACKET) - } - return matchCount + return signatureString?.count { it == OPEN_BRACKET } ?: 0 } - /** - * Counts the number of > in this signature. - * - * @param signatureString - * @return - */ /** * Counts the number of > in this signature. * * @return */ private fun closeBracketCount(signatureString: String? = genericSignature): Int { - var matchCount = 0 - if (signatureString != null) { - matchCount = StringUtils.countMatches(signatureString, CLOSE_BRACKET) - } - return matchCount + return signatureString?.count { it == CLOSE_BRACKET } ?: 0 } - /** - * Method returns if the number of < matches the number of > - * - * @param signatureString - * @return - */ /** * Method returns if the number of < matches the number of > * @@ -271,12 +236,6 @@ class GenericClassSignatureParser( return paired } - /** - * Simple check to make sure that closing brackets come after opening brackets. - * - * @param signatureString - * @return - */ /** * Simple check to make sure that closing brackets come after opening brackets. * From b9464adc66c2f78c44a019805bb1ac37bc96e661 Mon Sep 17 00:00:00 2001 From: Jonathan Percival Date: Wed, 11 Dec 2024 09:20:57 -0700 Subject: [PATCH 27/27] Remove unused assertion --- .../elm/requirements/fhir/DataRequirementsProcessorTest.java | 3 --- 1 file changed, 3 deletions(-) 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 7fdae08c6..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,7 +1930,6 @@ 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); @@ -1939,8 +1938,6 @@ private void assertEqualToExpectedModuleDefinitionLibrary( var jsonActual = parser.encodeResourceToString(actualModuleDefinitionLibrary); assertEquals(jsonExpected, jsonActual); - - // assertTrue(actualModuleDefinitionLibrary.equalsDeep(expectedModuleDefinitionLibrary)); } @Test