From b79b51fdb4fd4481a945768a55f04207880f73a5 Mon Sep 17 00:00:00 2001 From: isuruh15 Date: Wed, 20 Aug 2025 13:55:49 +0530 Subject: [PATCH 1/6] fix version bump issues in workflow. --- .github/workflows/cd.yml | 2 ++ ballerina/pom.xml | 2 +- native/fhir-to-bal-lib/pom.xml | 2 +- native/fhir-to-bal-template/pom.xml | 2 +- native/health-cli/pom.xml | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 9e85b7a..4d744df 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -123,6 +123,8 @@ jobs: sed -i "0,/${CURRENT_VERSION}<\/version>/s//${NEW_VERSION}<\/version>/" native/fhir-to-bal-lib/pom.xml sed -i "0,/${CURRENT_VERSION}<\/version>/s//${NEW_VERSION}<\/version>/" native/fhir-to-bal-template/pom.xml sed -i "0,/${CURRENT_VERSION}<\/version>/s//${NEW_VERSION}<\/version>/" native/health-cli/pom.xml + sed -i "0,/${CURRENT_VERSION}<\/version>/s//${NEW_VERSION}<\/version>/" native/cds-bal-template/pom.xml + sed -i "0,/${CURRENT_VERSION}<\/version>/s//${NEW_VERSION}<\/version>/" native/health-tool-ballerina/pom.xml - name: Commit changes and make a PR if: ${{ inputs.bal_central_environment == 'PROD' }} working-directory: fhir-tools diff --git a/ballerina/pom.xml b/ballerina/pom.xml index e4dd305..3816340 100644 --- a/ballerina/pom.xml +++ b/ballerina/pom.xml @@ -29,7 +29,7 @@ ../pom.xml health-tool-ballerina - 3.1.1 + 3.1.2 ${project.version} diff --git a/native/fhir-to-bal-lib/pom.xml b/native/fhir-to-bal-lib/pom.xml index 7e2712a..ba82317 100644 --- a/native/fhir-to-bal-lib/pom.xml +++ b/native/fhir-to-bal-lib/pom.xml @@ -30,7 +30,7 @@ fhir-to-bal-lib - 3.1.1 + 3.1.2 diff --git a/native/fhir-to-bal-template/pom.xml b/native/fhir-to-bal-template/pom.xml index da68be3..0f19131 100644 --- a/native/fhir-to-bal-template/pom.xml +++ b/native/fhir-to-bal-template/pom.xml @@ -29,7 +29,7 @@ fhir-to-bal-template - 3.1.1 + 3.1.2 diff --git a/native/health-cli/pom.xml b/native/health-cli/pom.xml index e0a6436..0cb5fca 100644 --- a/native/health-cli/pom.xml +++ b/native/health-cli/pom.xml @@ -29,7 +29,7 @@ ../../pom.xml health-cli - 3.1.1 + 3.1.2 From 8124e1deb6639cd5facba4438cf2abcac6e21f30 Mon Sep 17 00:00:00 2001 From: "A.M.S.I Attanayake" Date: Thu, 4 Sep 2025 14:06:14 +0530 Subject: [PATCH 2/6] minor bug fixes for health tool --- .../AbstractResourceContextGenerator.java | 15 ++----- .../r4/R4DatatypeContextGenerator.java | 7 +++- .../r4/R4ResourceContextGenerator.java | 1 + .../r5/R5DatatypeContextGenerator.java | 9 ++++- .../r5/R5ResourceContextGenerator.java | 1 + .../ResourceTemplateGenerator.java | 40 ++++++++++++++++--- .../packagegen/tool/utils/GeneratorUtils.java | 35 ++++++++++++++++ .../main/resources/templates/fhir_resource.vm | 8 +++- .../project/tool/model/SearchParam.java | 4 +- .../versions/r4/R4BallerinaProjectTool.java | 4 +- .../versions/r5/R5BallerinaProjectTool.java | 4 +- .../health-cli/src/test/java/TestRunner.java | 3 +- 12 files changed, 103 insertions(+), 28 deletions(-) diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractResourceContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractResourceContextGenerator.java index 07ac2ce..2679154 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractResourceContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractResourceContextGenerator.java @@ -111,17 +111,8 @@ protected void checkAndAddConstraintImport(Element element) { * @param element The element to check for international imports. */ protected void checkAndAddInternationalImport(Element element) { - boolean isImportedFromInternational = false; - if (element.getContentReference() != null && GeneratorUtils.isReferredFromInternational(element.getContentReference())) { - isImportedFromInternational = true; - } else if (element.hasChildElements()) { - for (Element childElement : element.getChildElements().values()) { - if (childElement.getContentReference() != null && GeneratorUtils.isReferredFromInternational(childElement.getContentReference())) { - isImportedFromInternational = true; - break; - } - } - } + boolean isImportedFromInternational = GeneratorUtils.checkForInternationalImports(element); + boolean isInternationalImportExists = this.resourceTemplateContextInstance.getResourceDependencies() .stream() .anyMatch(d -> d.equals(this.toolConfig.getPackageConfig().getInternationalPackage())); @@ -183,7 +174,7 @@ private void validateAndPopulateExtendedElement(Element element) { extendedElement = GeneratorUtils.getInstance().populateExtendedElement(element, BallerinaDataType.Enum, elementDataType, this.resourceTemplateContextInstance.getResourceName()); putExtendedElementIfAbsent(element, extendedElement); - } else if (element.isSlice() || elementDataType.equals("BackboneElement") || elementDataType.equals("BackboneType") || (element.isExtended() && element.hasChildElements())) { + } else if (element.isSlice() || elementDataType.equals("BackboneElement") || elementDataType.equals("BackboneType") || (element.isExtended() && element.hasChildElements() && !GeneratorUtils.isPrimitiveElement(elementDataType))) { extendedElement = GeneratorUtils.getInstance().populateExtendedElement(element, BallerinaDataType.Record, elementDataType, this.resourceTemplateContextInstance.getResourceName()); extendedElement.setElements(element.getChildElements()); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java index e6748b6..9b0b6b4 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java @@ -63,7 +63,12 @@ protected void populateDatatypeContext() { } else if ("draft".equalsIgnoreCase(datatypeDefn.getDefinition().getStatus().toString()) && (datatypeDefn.getDefinition().getName().contains(" ") && datatypeDefn.getDefinition().getName().split(" ").length > 0)) { - continue; + String[] elementNameContent = datatypeDefn.getDefinition().getName().split(" "); + + // Excludes generation of DataElement constraints (e.g.: ElementDefinition-de) + if (Arrays.asList(elementNameContent).contains("DataElement")) { + continue; + } } DatatypeTemplateContext context = new DatatypeTemplateContext(); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ResourceContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ResourceContextGenerator.java index ef5aa59..2bcd5d3 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ResourceContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ResourceContextGenerator.java @@ -81,6 +81,7 @@ protected void populateResourceTemplateContexts(FHIRImplementationGuide ig) { this.resourceTemplateContextInstance.setResourceName(GeneratorUtils.getInstance().resolveSpecialCharacters(structureDefinition.getName())); this.resourceTemplateContextInstance.setProfile(resourceDef.getDefinition().getUrl()); this.resourceTemplateContextInstance.setIgName(ig.getName()); + this.resourceTemplateContextInstance.setBaseIgName(structureDefinition.getBaseDefinition()); ResourceDefinitionAnnotation resourceDefinitionAnnotation = new ResourceDefinitionAnnotation(); resourceDefinitionAnnotation.setName(GeneratorUtils.getInstance().resolveSpecialCharacters(structureDefinition.getName())); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5DatatypeContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5DatatypeContextGenerator.java index cbcfe72..4317d40 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5DatatypeContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5DatatypeContextGenerator.java @@ -49,7 +49,7 @@ public class R5DatatypeContextGenerator extends AbstractDatatypeContextGenerator "DataType", "TriggerDefinition", "ParameterDefinition", "ExtendedContactDetail", "Availability", "Expression", "UsageContext", "MonetaryComponent", "VirtualServiceDetail", "CodeableReference", "Meta", "Reference", "Dosage", "xhtml", "Narrative", - "Extension", "ElementDefinition" + "Extension", "ElementDefinition", "Base" ); public R5DatatypeContextGenerator(FHIRSpecificationData fhirSpecificationData) { @@ -67,7 +67,12 @@ protected void populateDatatypeContext() { } else if ("draft".equalsIgnoreCase(datatypeDefn.getDefinition().getStatus().toString()) && (datatypeDefn.getDefinition().getName().contains(" ") && datatypeDefn.getDefinition().getName().split(" ").length > 0)) { - continue; + String[] elementNameContent = datatypeDefn.getDefinition().getName().split(" "); + + // Excludes generation of DataElement constraints (e.g.: ElementDefinition-de) + if (Arrays.asList(elementNameContent).contains("DataElement")) { + continue; + } } DatatypeTemplateContext context = new DatatypeTemplateContext(); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ResourceContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ResourceContextGenerator.java index 99b5b72..d942b8b 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ResourceContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ResourceContextGenerator.java @@ -82,6 +82,7 @@ protected void populateResourceTemplateContexts(FHIRImplementationGuide ig) { this.resourceTemplateContextInstance.setResourceName(GeneratorUtils.getInstance().resolveSpecialCharacters(structureDefinition.getName())); this.resourceTemplateContextInstance.setProfile(resourceDef.getDefinition().getUrl()); this.resourceTemplateContextInstance.setIgName(ig.getName()); + this.resourceTemplateContextInstance.setBaseIgName(structureDefinition.getBaseDefinition()); ResourceDefinitionAnnotation resourceDefinitionAnnotation = new ResourceDefinitionAnnotation(); resourceDefinitionAnnotation.setName(GeneratorUtils.getInstance().resolveSpecialCharacters(structureDefinition.getName())); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java index dd12944..4a8cf97 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java @@ -25,6 +25,8 @@ import org.wso2.healthcare.codegen.tool.framework.commons.exception.CodeGenException; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.ToolConstants; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.config.BallerinaPackageGenToolConfig; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DataTypeProfile; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.Element; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.PackageTemplateContext; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ResourceTemplateContext; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.utils.CommonUtil; @@ -32,12 +34,7 @@ import org.wso2.healthcare.codegen.tool.framework.fhir.core.AbstractFHIRTemplateGenerator; import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; /** * Generator class for resource related template context @@ -151,6 +148,37 @@ private TemplateContext createTemplateContextForResourceSkeletons(ResourceTempla } resourceDependencies.addAll(resourceTemplateContext.getResourceDependencies()); + + // Store element datatype prefixes other than baseImport and internationalImport + Set prefixes = new HashSet<>(); + for (Element element : resourceTemplateContext.getSnapshotElements().values()) { + Map profiles = element.getProfiles(); + for (Map.Entry entry : profiles.entrySet()) { + if (entry.getValue().getPrefix() != null && !element.getPath().contains("extension")) { + prefixes.add(entry.getValue().getPrefix()); + } + } + } + + // Remove unused packages from dependent-ig mode + if (prefixes.isEmpty()) { + Iterator iterator = resourceDependencies.iterator(); + while (iterator.hasNext()) { + String dependency = iterator.next(); + if (dependency.endsWith(this.resourceProperties.get("basePackageIdentifier").toString()) + || dependency.endsWith(this.resourceProperties.get("internationalPackageIdentifier").toString()) + || dependency.contains("constraint")) { + } else { + iterator.remove(); + } + } + } + + // remove international package if the current IG is the international package + if (packageContext.getIgTemplateContext().getIgName().contains(this.resourceProperties.get("internationalPackageIdentifier").toString())) { + resourceDependencies.remove(this.packageTemplateContext.getInternationalPackageName()); + } + templateContext.setProperty("imports", resourceDependencies); return templateContext; } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java index 60d03d7..70ce79e 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java @@ -327,6 +327,7 @@ public ExtendedElement populateExtendedElement(Element element, BallerinaDataTyp if (element.getContentReference() != null && !isReferredFromInternational(element.getContentReference())) { // Avoid creation of a new identifiers for referred elements + // todo: revise generation of CDSHooksRequestOrchestrationActionAction and CDSHooksServicePlanDefinitionActionAction in international500 extendedElementTypeName = getReferringElementName(element.getContentReference(), false, typeNamePrefix); } else { extendedElementTypeName = generateExtendedElementIdentifier(element, typeNamePrefix); @@ -352,6 +353,7 @@ public ExtendedElement populateExtendedElement(Element element, BallerinaDataTyp // The base type "code" is ignored because it converts to an ENUM by default extendedElement.setPrimitiveExtendedType(baseType); + element.setDataType(baseType); } LOG.debug("Ended: Resource Extended Element population"); @@ -623,4 +625,37 @@ public static String getReferringElementName(String contentReference, boolean is } return referringElementName; } + + /** + * Recursively checks if an element or any of its child elements refer to an international specification. + * + * @param element The element to check. + * @return true if the element or any of its child elements refer to an international specification, + * false otherwise. + */ + public static boolean checkForInternationalImports(Element element) { + if (element == null) { + return false; + } + + // Check current element's content reference + if (element.getContentReference() != null && + GeneratorUtils.isReferredFromInternational(element.getContentReference())) { + return true; + } + + // Base case - no child elements + if (!element.hasChildElements()) { + return false; + } + + // Recursively check all child elements + for (Element childElement : element.getChildElements().values()) { + if (checkForInternationalImports(childElement)) { + return true; + } + } + + return false; + } } diff --git a/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm b/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm index 862a67b..323c94b 100644 --- a/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm +++ b/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm @@ -105,7 +105,7 @@ public type $util.resolveSpecialCharacters($resourceName) record {| } $tab#end #set($isDataTypeSet = false) - #foreach($profileEntry in $element.getProfiles().entrySet())#if(!$dataTypes.contains($profileEntry.value.getProfileType()))#if(!$profileEntry.value.getPrefix())#if($util.isReferredFromInternational($element.getContentReference()))${internationalImportIdentifier}#else${importIdentifier}#end#else$profileEntry.value.getPrefix():#end#end#if($element.isExtended())#foreach($key in $extendedElements.keySet())#if($key == $profileEntry.value.getProfileType() && $extendedElements.get($key).getPrimitiveExtendedType())$util.resolveSpecialCharacters($profileEntry.value.getProfileType()) | ${importIdentifier}$extendedElements.get($key).getPrimitiveExtendedType()#set($isDataTypeSet = true)#end#end#end#if(!$isDataTypeSet)$util.resolveSpecialCharacters($profileEntry.value.getProfileType())#end#if($element.isArray()) []#end#if($foreach.count != $element.getProfiles().size())|#end#end $util.resolveKeywordConflict($util.resolveSpecialCharacters($element.getName()))#if(!$element.isRequired())?#end; + #foreach($profileEntry in $element.getProfiles().entrySet())#if(!$dataTypes.contains($profileEntry.value.getProfileType()))#if(!$profileEntry.value.getPrefix())#if($util.isReferredFromInternational($element.getContentReference()))${internationalImportIdentifier}#else${importIdentifier}#end#else$profileEntry.value.getPrefix():#end#end#if($element.isExtended())#foreach($key in $extendedElements.keySet())#if($key == $profileEntry.value.getProfileType() && $extendedElements.get($key).getPrimitiveExtendedType())${importIdentifier}$extendedElements.get($key).getPrimitiveExtendedType()#set($isDataTypeSet = true)#end#end#end#if(!$isDataTypeSet)$util.resolveSpecialCharacters($profileEntry.value.getProfileType())#end#if($element.isArray()) []#end#if($foreach.count != $element.getProfiles().size())|#end#end $util.resolveKeywordConflict($util.resolveSpecialCharacters($element.getName()))#if(!$element.isRequired())?#end; #end ${importIdentifier}Element ...; |}; @@ -193,9 +193,15 @@ $tab#end #elseif($extendedElement.getBalDataType().getType() == "enum") # $extendedElement.getTypeName() enum public enum $util.resolveSpecialCharacters($extendedElement.getTypeName()) { + #if($basePackageIdentifier == "r4") #foreach($childElement in $extendedElement.getElements()) CODE_$childElement.getRootElementName().toUpperCase()_$util.resolveSpecialCharacters($childElement.getName()).toUpperCase() = "$childElement.getName()"#if($foreach.count != $extendedElement.getElements().size()),#end #end + #else + #foreach($childElement in $extendedElement.getElements()) + CODE_${resourceName.toUpperCase()}_$childElement.getRootElementName().toUpperCase()_$util.resolveSpecialCharacters($childElement.getName()).toUpperCase() = "$childElement.getName()"#if($foreach.count != $extendedElement.getElements().size()),#end + #end + #end } #end diff --git a/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/model/SearchParam.java b/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/model/SearchParam.java index 4ca4cbd..48e5f3d 100644 --- a/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/model/SearchParam.java +++ b/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/model/SearchParam.java @@ -83,7 +83,9 @@ private String extractDescription(String originalDescription) { } } } else { - return originalDescription.replace("\n", ""); + originalDescription = originalDescription.replace("\n", ""); + originalDescription = originalDescription.replace("\r", ""); + return originalDescription; } return originalDescription; } diff --git a/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r4/R4BallerinaProjectTool.java b/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r4/R4BallerinaProjectTool.java index bf505fe..834091d 100644 --- a/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r4/R4BallerinaProjectTool.java +++ b/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r4/R4BallerinaProjectTool.java @@ -61,10 +61,10 @@ protected void populateBalService() { for (Map.Entry resourceEntry : entry.getValue().getResources().entrySet()) { String key = resourceEntry.getKey(); FHIRR4ResourceDef resourceDef = (FHIRR4ResourceDef) resourceEntry.getValue(); - String resourceName = resourceDef.getDefinition().getName(); String resourceKind = resourceDef.getDefinition().getKind().toCode(); + String resourceType = resourceDef.getDefinition().getType(); - if (!getExcludedFHIRApis().contains(resourceName) && resourceKind.equalsIgnoreCase("RESOURCE")) { + if (!getExcludedFHIRApis().contains(resourceType) && resourceKind.equalsIgnoreCase("RESOURCE")) { resourceDefMap.put(key, resourceDef); } } diff --git a/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r5/R5BallerinaProjectTool.java b/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r5/R5BallerinaProjectTool.java index 82a0a49..92b17fb 100644 --- a/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r5/R5BallerinaProjectTool.java +++ b/native/fhir-to-bal-template/src/main/java/org/wso2/healthcare/fhir/codegen/ballerina/project/tool/versions/r5/R5BallerinaProjectTool.java @@ -61,10 +61,10 @@ protected void populateBalService() { for (Map.Entry resourceEntry : entry.getValue().getResources().entrySet()) { String key = resourceEntry.getKey(); FHIRR5ResourceDef resourceDef = (FHIRR5ResourceDef) resourceEntry.getValue(); - String resourceName = resourceDef.getDefinition().getName(); String resourceKind = resourceDef.getDefinition().getKind().toCode(); + String resourceType = resourceDef.getDefinition().getType(); - if (!getExcludedFHIRApis().contains(resourceName) && resourceKind.equalsIgnoreCase("RESOURCE")) { + if (!getExcludedFHIRApis().contains(resourceType) && resourceKind.equalsIgnoreCase("RESOURCE")) { resourceDefMap.put(key, resourceDef); } } diff --git a/native/health-cli/src/test/java/TestRunner.java b/native/health-cli/src/test/java/TestRunner.java index 9d9cfae..9bd990b 100644 --- a/native/health-cli/src/test/java/TestRunner.java +++ b/native/health-cli/src/test/java/TestRunner.java @@ -34,7 +34,8 @@ public class TestRunner { private static final Path executionPath = Paths.get(System.getProperty("user.dir")); public static void main(String[] args) { - // System.setProperty("fhirVersion", "r4"); // Uncomment if directly running the TestRunner.java + /// Uncomment if directly running the TestRunner.java + // System.setProperty("fhirVersion", "r4"); try { runTestForCdsTool(); if (System.getProperty("fhirVersion").equalsIgnoreCase("r4")) { From 04c04acfbcb9a87482bef9905943cebc951b7471 Mon Sep 17 00:00:00 2001 From: "A.M.S.I Attanayake" Date: Wed, 27 Aug 2025 11:23:59 +0530 Subject: [PATCH 3/6] add extensions generation for packages --- .../tool/BallerinaPackageGenTool.java | 1 + .../tool/model/ExtensionTemplateContext.java | 60 ++ .../tool/model/PackageTemplateContext.java | 10 +- .../tool/model/ResourceTemplateContext.java | 10 + .../AbstractDatatypeContextGenerator.java | 10 +- .../AbstractExtensionContextGenerator.java | 99 ++ .../AbstractPackageContextGenerator.java | 3 + .../r4/R4DatatypeContextGenerator.java | 2 +- .../r4/R4ExtensionContextGenerator.java | 221 +++++ .../r4/R4PackageContextGenerator.java | 16 +- .../r5/R5DatatypeContextGenerator.java | 2 +- .../r5/R5ExtensionContextGenerator.java | 221 +++++ .../r5/R5PackageContextGenerator.java | 10 +- .../ExtensionTemplateGenerator.java | 76 ++ .../templategen/PackageTemplateGenerator.java | 21 +- .../ResourceTemplateGenerator.java | 45 +- .../packagegen/tool/utils/GeneratorUtils.java | 57 ++ .../main/resources/templates/extensions.vm | 88 ++ .../main/resources/templates/fhir_resource.vm | 6 + ...finition-eu-core-ext-patient-religion.json | 903 ++++++++++++++++++ 20 files changed, 1836 insertions(+), 25 deletions(-) create mode 100644 native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java create mode 100644 native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java create mode 100644 native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java create mode 100644 native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java create mode 100644 native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ExtensionTemplateGenerator.java create mode 100644 native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm create mode 100644 native/health-cli/src/test/resources/profiles.EuropeBase/StructureDefinition-eu-core-ext-patient-religion.json diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/BallerinaPackageGenTool.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/BallerinaPackageGenTool.java index 03b1ea7..b305eea 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/BallerinaPackageGenTool.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/BallerinaPackageGenTool.java @@ -76,6 +76,7 @@ public TemplateGenerator execute(ToolContext toolContext) throws CodeGenExceptio } properties.put("packageContext", packageContextGenerator.getPackageContext()); properties.put("datatypeContext", packageContextGenerator.getPackageContext().getDatatypeTemplateContextMap()); + properties.put("extensionContext", packageContextGenerator.getPackageContext().getExtensionTemplateContext()); packageTemplateGenerator.setGeneratorProperties(properties); return packageTemplateGenerator; } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java new file mode 100644 index 0000000..08176ff --- /dev/null +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.healthcare.fhir.ballerina.packagegen.tool.model; + +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.HashMap; + +public class ExtensionTemplateContext { + private Map extensionDatatypes; + private Map> extensionSlices; + private Map> extensionResources; + + public ExtensionTemplateContext() { + extensionDatatypes = new TreeMap<>(); + extensionSlices = new HashMap<>(); + extensionResources = new HashMap<>(); + } + + public Map getExtensionDatatypes() { + return extensionDatatypes; + } + + public void setExtensionDatatypes(Map extensionDatatypes) { + this.extensionDatatypes = extensionDatatypes; + } + + public Map> getExtensionSlices() { + return extensionSlices; + } + + public void setExtensionSlices(Map> extensionSlices) { + this.extensionSlices = extensionSlices; + } + + public Map> getExtensionResources() { + return extensionResources; + } + + public void setExtensionResources(Map> extensionResources) { + this.extensionResources = extensionResources; + } +} diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/PackageTemplateContext.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/PackageTemplateContext.java index 2b00eec..479991b 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/PackageTemplateContext.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/PackageTemplateContext.java @@ -18,7 +18,6 @@ package org.wso2.healthcare.fhir.ballerina.packagegen.tool.model; -import java.util.List; import java.util.Map; import java.util.Set; @@ -31,6 +30,7 @@ public class PackageTemplateContext { private String internationalPackageName; private Map resourceTemplateContextMap; private Map datatypeTemplateContextMap; + private ExtensionTemplateContext extensionTemplateContext; private Map resourceNameTypeMap; private Set dataTypesRegistry; private Map dependenciesMap; @@ -80,6 +80,14 @@ public void addDatatypeTemplateContext(String datatypeName, DatatypeTemplateCont this.datatypeTemplateContextMap.putIfAbsent(datatypeName, context); } + public ExtensionTemplateContext getExtensionTemplateContext() { + return extensionTemplateContext; + } + + public void setExtensionTemplateContext(ExtensionTemplateContext extensionTemplateContext) { + this.extensionTemplateContext = extensionTemplateContext; + } + public Map getResourceNameTypeMap() { return resourceNameTypeMap; } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ResourceTemplateContext.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ResourceTemplateContext.java index 51757f4..a457e46 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ResourceTemplateContext.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ResourceTemplateContext.java @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.Map; /** * Class holder for resource related template context @@ -44,6 +45,7 @@ public class ResourceTemplateContext { private HashMap> sliceElements = new HashMap<>(); private HashMap extendedElements = new HashMap<>(); private Set resourceDependencies = new HashSet<>(); + private Map> resourceExtensions = new HashMap<>(); public String getResourceType() { return resourceType; @@ -148,4 +150,12 @@ public Set getResourceDependencies() { public void setResourceDependencies(Set resourceDependencies) { this.resourceDependencies = resourceDependencies; } + + public Map> getResourceExtensions() { + return resourceExtensions; + } + + public void setResourceExtensions(Map> resourceExtensions) { + this.resourceExtensions = resourceExtensions; + } } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractDatatypeContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractDatatypeContextGenerator.java index a029f64..fb7a4bb 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractDatatypeContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractDatatypeContextGenerator.java @@ -48,19 +48,11 @@ public AbstractDatatypeContextGenerator(FHIRSpecificationData fhirSpecificationD populateDatatypeContext(); } - public Map getDatatypeDefnMap() { - return datatypeDefnMap; - } - - public Map getDataTypeTemplateContextMap() { - return dataTypeTemplateContextMap; - } - protected Map getDataTypeDefnMap() { return datatypeDefnMap; } - protected Map datatypeTemplateContextMap() { + public Map getDatatypeTemplateContextMap() { return dataTypeTemplateContextMap; } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java new file mode 100644 index 0000000..ac64727 --- /dev/null +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.healthcare.fhir.ballerina.packagegen.tool.modelgen; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.healthcare.codegen.tool.framework.fhir.core.common.FHIRSpecificationData; +import org.wso2.healthcare.codegen.tool.framework.fhir.core.model.FHIRDataTypeDef; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.DataTypesRegistry; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DatatypeTemplateContext; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ExtensionTemplateContext; + +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.util.HashMap; + +public abstract class AbstractExtensionContextGenerator { + private static final Log LOG = LogFactory.getLog(AbstractExtensionContextGenerator.class); + private final Map extensionDefnMap; + private final ExtensionTemplateContext extensionTemplateContext; + + public AbstractExtensionContextGenerator(FHIRSpecificationData fhirSpecificationData) { + LOG.info("Started: Extensions Generation"); + + this.extensionDefnMap = fhirSpecificationData.getDataTypes(); + this.extensionTemplateContext = new ExtensionTemplateContext(); + + populateExtensionTemplateContext(); + + LOG.info("Ended: Extensions Generation"); + } + + protected Map getExtensionDefnMap() { + return extensionDefnMap; + } + + public ExtensionTemplateContext getExtensionTemplateContext() { + return extensionTemplateContext; + } + + private void populateExtensionTemplateContext() { + populateBaseExtensionContext(); + populateSliceExtensionContext(); + populateExtensionSliceMap(); + } + + protected abstract void populateBaseExtensionContext(); + + protected abstract void populateSliceExtensionContext(); + + protected abstract void populateExtensionResourceMap(String identifier, FHIRDataTypeDef extensionDef); + + protected void populateExtensionSliceMap() { + Map> childExtensionMap = new HashMap<>(); + Set searchKeys = new HashSet<>(); + Map extensionDatatypeMap = extensionTemplateContext.getExtensionDatatypes(); + + for (Map.Entry contextEntry : extensionDatatypeMap.entrySet()) { + if (contextEntry.getKey().contains("http://")) { + /// Set Base Extension + searchKeys.add(contextEntry.getValue().getName()); + String extensionArrName = contextEntry.getValue().getName() + "Extensions"; + childExtensionMap.putIfAbsent(extensionArrName, new HashSet<>()); + DataTypesRegistry.getInstance().addDataType(extensionArrName); + } + } + + for (Map.Entry contextEntry : extensionDatatypeMap.entrySet()) { + /// Add Slice Extensions + /// Ensure that http:// keys are processed first + for (String key : searchKeys) { + if (contextEntry.getKey().contains(key.toLowerCase())) { + childExtensionMap.get(key + "Extensions").add(contextEntry.getValue().getName()); + } + } + + } + + childExtensionMap.entrySet().removeIf(entry -> entry.getValue().isEmpty()); + extensionTemplateContext.setExtensionSlices(childExtensionMap); + } +} diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractPackageContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractPackageContextGenerator.java index 7c7d88f..eaa33f3 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractPackageContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractPackageContextGenerator.java @@ -79,6 +79,7 @@ private void populatePackageContext(Map igEntri populateDatatypeTemplateContext(specificationData); populateResourceTemplateContext(implementationGuide); + populateExtensionTemplateContext(specificationData); populateIGTemplateContexts(entry.getValue().getName(), implementationGuide); } LOG.debug("Ended: Package Context population"); @@ -94,6 +95,8 @@ public BallerinaPackageGenToolConfig getToolConfig() { protected abstract void populateDatatypeTemplateContext(FHIRSpecificationData specificationData); + protected abstract void populateExtensionTemplateContext(FHIRSpecificationData specificationData); + protected abstract void populateResourceTemplateContext(FHIRImplementationGuide implementationGuide); protected abstract void populateIGTemplateContexts(String igCode, FHIRImplementationGuide implementationGuide); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java index 9b0b6b4..cc8e49a 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4DatatypeContextGenerator.java @@ -124,7 +124,7 @@ protected void populateDatatypeContext() { context.addElement(element); } } - datatypeTemplateContextMap().putIfAbsent(datatypeDefn.getDefinition().getUrl(), context); + getDatatypeTemplateContextMap().putIfAbsent(datatypeDefn.getDefinition().getUrl(), context); } } } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java new file mode 100644 index 0000000..7cd13f4 --- /dev/null +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.healthcare.fhir.ballerina.packagegen.tool.modelgen.versions.r4; + +import org.hl7.fhir.r4.model.ElementDefinition; +import org.hl7.fhir.r4.model.StructureDefinition; +import org.wso2.healthcare.codegen.tool.framework.fhir.core.common.FHIRSpecificationData; +import org.wso2.healthcare.codegen.tool.framework.fhir.core.model.FHIRDataTypeDef; +import org.wso2.healthcare.codegen.tool.framework.fhir.core.versions.r4.model.FHIRR4DataTypeDef; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.DataTypesRegistry; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.ToolConstants; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.AnnotationElement; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DataTypeDefinitionAnnotation; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DatatypeTemplateContext; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.Element; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.modelgen.AbstractExtensionContextGenerator; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.utils.CommonUtil; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.utils.GeneratorUtils; + +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.HashSet; +import java.util.ArrayList; + +public class R4ExtensionContextGenerator extends AbstractExtensionContextGenerator { + public R4ExtensionContextGenerator(FHIRSpecificationData specificationData) { + super(specificationData); + } + + @Override + public void populateBaseExtensionContext() { + for (Map.Entry extensionDefEntry : getExtensionDefnMap().entrySet()) { + + FHIRR4DataTypeDef r4ExtensionDefn = (FHIRR4DataTypeDef) extensionDefEntry.getValue(); + + if ("Extension".equals(r4ExtensionDefn.getDefinition().getType())) { + /// Extensions will also be treated as an extended data type. + DatatypeTemplateContext context = new DatatypeTemplateContext(); + String typeName = CommonUtil.getSplitTokenAt(r4ExtensionDefn.getDefinition().getUrl(), "/", ToolConstants.TokenPosition.END); + String elementIdentifier = GeneratorUtils.getInstance().getUniqueIdentifierFromId(typeName); + context.setName(elementIdentifier); + context.setBaseDataType("ExtensionExtension"); + + DataTypeDefinitionAnnotation annotation = new DataTypeDefinitionAnnotation(); + annotation.setName(elementIdentifier); + context.setAnnotation(annotation); + + for (ElementDefinition elementDefinition : r4ExtensionDefn.getDefinition().getSnapshot().getElement()) { + if (elementDefinition.getPath().contains(".") && !elementDefinition.getId().contains(":")) { + /// Parsing first level extensions + String elementName = elementDefinition.getPath().substring( + elementDefinition.getPath().lastIndexOf(".") + 1); + + if (elementDefinition.getMin() != 0 && !elementDefinition.getMax().equals("0")) { + Element element = new Element(); + element.setMax(GeneratorUtils.getMaxCardinality(elementDefinition.getMax())); + element.setMin(elementDefinition.getMin()); + element.setArray(!"0".equals(elementDefinition.getBase().getMax()) && !"1".equals(elementDefinition.getBase().getMax())); + + if ("url".equals(elementName) && elementDefinition.getFixed() != null) { + List fixedValues = new ArrayList<>(); + String value = elementDefinition.getFixed().toString(); + value = value.replaceAll("UriType|\\[|\\]", ""); + fixedValues.add(value); + element.setFixedValue(fixedValues); + element.setDataType(value); + } + + String typeCode = elementDefinition.getType().get(0).getCode(); + if (typeCode == null) { + // Give a type for PrimitiveType Extensions marked with "_" + // E.g.: type [{_code:{...}}] + typeCode = "Extension"; + } + + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { + if (elementDefinition.getFixed() == null) { + element.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); + } + } else { + element.setDataType(typeCode); + } + + if (elementName.endsWith("[x]")) { + typeCode = elementDefinition.getType().get(0).getCode(); + element.setDataType(typeCode); + context.setBaseDataType(CommonUtil.toCamelCase(typeCode) + "Extension"); + } + + element.setName(GeneratorUtils.getInstance().resolveMultiDataTypeFieldNames(elementName, typeCode)); + element.setDescription(CommonUtil.parseMultilineString(elementDefinition.getDefinition())); + element.setPath(elementDefinition.getPath()); + + //populate annotations + AnnotationElement annotationElement = GeneratorUtils.getInstance().populateAnnotationElement(element); + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { + annotationElement.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); + } + annotation.addElement(annotationElement); + context.addElement(element); + } + } + } + populateExtensionResourceMap(elementIdentifier, extensionDefEntry.getValue()); + getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(r4ExtensionDefn.getDefinition().getUrl(), context); + DataTypesRegistry.getInstance().addDataType(context.getName()); + } + } + } + + @Override + public void populateSliceExtensionContext() { + for (Map.Entry extensionDefEntry : getExtensionDefnMap().entrySet()) { + FHIRR4DataTypeDef r4ExtensionDefn = (FHIRR4DataTypeDef) extensionDefEntry.getValue(); + String urlId = CommonUtil.getSplitTokenAt(r4ExtensionDefn.getDefinition().getUrl(), "/", ToolConstants.TokenPosition.END); + String rootExtensionName = GeneratorUtils.getInstance().getUniqueIdentifierFromId(urlId); + + if ("Extension".equals(r4ExtensionDefn.getDefinition().getType())) { + for (ElementDefinition elementDefinition : r4ExtensionDefn.getDefinition().getSnapshot().getElement()) { + if (elementDefinition.getId().contains(":")) { + if (elementDefinition.getSliceName() != null) { + String rootSliceName = elementDefinition.getSliceName(); + + String contextName = rootExtensionName + CommonUtil.toCamelCase(elementDefinition.getSliceName()); + contextName = GeneratorUtils.getInstance().getUniqueIdentifierFromId(contextName); + + DatatypeTemplateContext context = new DatatypeTemplateContext(); + context.setName(contextName); + context.setBaseDataType(r4ExtensionDefn.getDefinition().getType() + "Extension"); + + DataTypeDefinitionAnnotation annotation = new DataTypeDefinitionAnnotation(); + annotation.setName(contextName); + context.setAnnotation(annotation); + + for (ElementDefinition sliceElementDefinition : r4ExtensionDefn.getDefinition().getSnapshot().getElement()) { + if (sliceElementDefinition.getId().contains(rootSliceName)) { + String[] childSliceNames = sliceElementDefinition.getId().split("[:.]"); + String childSliceName = childSliceNames[childSliceNames.length - 1]; + + if (rootSliceName.equals(childSliceName)) { + continue; + } + + Element element = new Element(); + element.setMax(GeneratorUtils.getMaxCardinality(sliceElementDefinition.getMax())); + element.setMin(sliceElementDefinition.getMin()); + element.setArray(!"0".equals(sliceElementDefinition.getBase().getMax()) && !"1".equals(sliceElementDefinition.getBase().getMax())); + + String typeCode = sliceElementDefinition.getType().get(0).getCode(); + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { + element.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); + } else { + element.setDataType(typeCode); + } + + if (childSliceName.endsWith("[x]")) { + typeCode = sliceElementDefinition.getType().get(0).getCode(); + element.setDataType(typeCode); + context.setBaseDataType(CommonUtil.toCamelCase(typeCode) + "Extension"); + } + + if ("url".equals(childSliceName)) { + if (sliceElementDefinition.hasFixed()) { + List fixedValues = new ArrayList<>(); + String value = sliceElementDefinition.getFixed().toString().replaceAll("UriType|\\[|\\]", ""); + fixedValues.add(value); + element.setFixedValue(fixedValues); + } + } + + element.setName(GeneratorUtils.getInstance().resolveMultiDataTypeFieldNames(childSliceName, typeCode)); + element.setDescription(CommonUtil.parseMultilineString(sliceElementDefinition.getDefinition())); + element.setPath(rootSliceName + "." + childSliceName); + + //populate annotations + AnnotationElement annotationElement = GeneratorUtils.getInstance().populateAnnotationElement(element); + if (GeneratorUtils.getInstance().shouldReplacedByBalType(annotationElement.getDataType())) { + annotationElement.setDataType(GeneratorUtils.getInstance().resolveDataType(annotationElement.getDataType())); + } + annotation.addElement(annotationElement); + context.addElement(element); + } + } + getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(contextName.toLowerCase(), context); + DataTypesRegistry.getInstance().addDataType(context.getName()); + } + } + } + } + } + } + + @Override + public void populateExtensionResourceMap(String identifier, FHIRDataTypeDef extensionDef) { + Set extensionContext = new HashSet<>(); + + /// Retrieve context of extensions + FHIRR4DataTypeDef r4ExtensionDefinition = (FHIRR4DataTypeDef) extensionDef; + for (StructureDefinition.StructureDefinitionContextComponent contextComponent : r4ExtensionDefinition.getDefinition().getContext()) { + extensionContext.add(contextComponent.getExpression()); + } + getExtensionTemplateContext().getExtensionResources().putIfAbsent(identifier, extensionContext); + } +} \ No newline at end of file diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4PackageContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4PackageContextGenerator.java index ea83cfe..db1030f 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4PackageContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4PackageContextGenerator.java @@ -21,6 +21,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.healthcare.codegen.tool.framework.commons.core.SpecificationData; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ExtensionTemplateContext; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.SearchParameter; import org.wso2.healthcare.codegen.tool.framework.fhir.core.common.FHIRSpecificationData; import org.wso2.healthcare.codegen.tool.framework.fhir.core.versions.r4.model.FHIRR4SearchParamDef; @@ -47,14 +48,27 @@ public R4PackageContextGenerator(BallerinaPackageGenToolConfig config, Map extensionDefEntry : getExtensionDefnMap().entrySet()) { + + FHIRR5DataTypeDef r5ExtensionDefn = (FHIRR5DataTypeDef) extensionDefEntry.getValue(); + + if ("Extension".equals(r5ExtensionDefn.getDefinition().getType())) { + /// Extensions will also be treated as an extended data type. + DatatypeTemplateContext context = new DatatypeTemplateContext(); + String typeName = CommonUtil.getSplitTokenAt(r5ExtensionDefn.getDefinition().getUrl(), "/", ToolConstants.TokenPosition.END); + String elementIdentifier = GeneratorUtils.getInstance().getUniqueIdentifierFromId(typeName); + context.setName(elementIdentifier); + context.setBaseDataType("ExtensionExtension"); + + DataTypeDefinitionAnnotation annotation = new DataTypeDefinitionAnnotation(); + annotation.setName(elementIdentifier); + context.setAnnotation(annotation); + + for (ElementDefinition elementDefinition : r5ExtensionDefn.getDefinition().getSnapshot().getElement()) { + if (elementDefinition.getPath().contains(".") && !elementDefinition.getId().contains(":")) { + /// Parsing first level extensions + String elementName = elementDefinition.getPath().substring( + elementDefinition.getPath().lastIndexOf(".") + 1); + + if (elementDefinition.getMin() != 0 && !elementDefinition.getMax().equals("0")) { + Element element = new Element(); + element.setMax(GeneratorUtils.getMaxCardinality(elementDefinition.getMax())); + element.setMin(elementDefinition.getMin()); + element.setArray(!"0".equals(elementDefinition.getBase().getMax()) && !"1".equals(elementDefinition.getBase().getMax())); + + if ("url".equals(elementName) && elementDefinition.getFixed() != null) { + List fixedValues = new ArrayList<>(); + String value = elementDefinition.getFixed().toString(); + value = value.replaceAll("UriType|\\[|\\]", ""); + fixedValues.add(value); + element.setFixedValue(fixedValues); + element.setDataType(value); + } + + String typeCode = elementDefinition.getType().get(0).getCode(); + if (typeCode == null) { + // Give a type for PrimitiveType Extensions marked with "_" + // E.g.: type [{_code:{...}}] + typeCode = "Extension"; + } + + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { + if (elementDefinition.getFixed() == null) { + element.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); + } + } else { + element.setDataType(typeCode); + } + + if (elementName.endsWith("[x]")) { + typeCode = elementDefinition.getType().get(0).getCode(); + element.setDataType(typeCode); + context.setBaseDataType(CommonUtil.toCamelCase(typeCode) + "Extension"); + } + + element.setName(GeneratorUtils.getInstance().resolveMultiDataTypeFieldNames(elementName, typeCode)); + element.setDescription(CommonUtil.parseMultilineString(elementDefinition.getDefinition())); + element.setPath(elementDefinition.getPath()); + + //populate annotations + AnnotationElement annotationElement = GeneratorUtils.getInstance().populateAnnotationElement(element); + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { + annotationElement.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); + } + annotation.addElement(annotationElement); + context.addElement(element); + } + } + } + populateExtensionResourceMap(elementIdentifier, extensionDefEntry.getValue()); + getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(r5ExtensionDefn.getDefinition().getUrl(), context); + DataTypesRegistry.getInstance().addDataType(context.getName()); + } + } + } + + @Override + public void populateSliceExtensionContext() { + for (Map.Entry extensionDefEntry : getExtensionDefnMap().entrySet()) { + FHIRR5DataTypeDef r5ExtensionDefn = (FHIRR5DataTypeDef) extensionDefEntry.getValue(); + String urlId = CommonUtil.getSplitTokenAt(r5ExtensionDefn.getDefinition().getUrl(), "/", ToolConstants.TokenPosition.END); + String rootExtensionName = GeneratorUtils.getInstance().getUniqueIdentifierFromId(urlId); + + if ("Extension".equals(r5ExtensionDefn.getDefinition().getType())) { + for (ElementDefinition elementDefinition : r5ExtensionDefn.getDefinition().getSnapshot().getElement()) { + if (elementDefinition.getId().contains(":")) { + if (elementDefinition.getSliceName() != null) { + String rootSliceName = elementDefinition.getSliceName(); + + String contextName = rootExtensionName + CommonUtil.toCamelCase(elementDefinition.getSliceName()); + contextName = GeneratorUtils.getInstance().getUniqueIdentifierFromId(contextName); + + DatatypeTemplateContext context = new DatatypeTemplateContext(); + context.setName(contextName); + context.setBaseDataType(r5ExtensionDefn.getDefinition().getType() + "Extension"); + + DataTypeDefinitionAnnotation annotation = new DataTypeDefinitionAnnotation(); + annotation.setName(contextName); + context.setAnnotation(annotation); + + for (ElementDefinition sliceElementDefinition : r5ExtensionDefn.getDefinition().getSnapshot().getElement()) { + if (sliceElementDefinition.getId().contains(rootSliceName)) { + String[] childSliceNames = sliceElementDefinition.getId().split("[:.]"); + String childSliceName = childSliceNames[childSliceNames.length - 1]; + + if (rootSliceName.equals(childSliceName)) { + continue; + } + + Element element = new Element(); + element.setMax(GeneratorUtils.getMaxCardinality(sliceElementDefinition.getMax())); + element.setMin(sliceElementDefinition.getMin()); + element.setArray(!"0".equals(sliceElementDefinition.getBase().getMax()) && !"1".equals(sliceElementDefinition.getBase().getMax())); + + String typeCode = sliceElementDefinition.getType().get(0).getCode(); + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { + element.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); + } else { + element.setDataType(typeCode); + } + + if (childSliceName.endsWith("[x]")) { + typeCode = sliceElementDefinition.getType().get(0).getCode(); + element.setDataType(typeCode); + context.setBaseDataType(CommonUtil.toCamelCase(typeCode) + "Extension"); + } + + if ("url".equals(childSliceName)) { + if (sliceElementDefinition.hasFixed()) { + List fixedValues = new ArrayList<>(); + String value = sliceElementDefinition.getFixed().toString().replaceAll("UriType|\\[|\\]", ""); + fixedValues.add(value); + element.setFixedValue(fixedValues); + } + } + + element.setName(GeneratorUtils.getInstance().resolveMultiDataTypeFieldNames(childSliceName, typeCode)); + element.setDescription(CommonUtil.parseMultilineString(sliceElementDefinition.getDefinition())); + element.setPath(rootSliceName + "." + childSliceName); + + //populate annotations + AnnotationElement annotationElement = GeneratorUtils.getInstance().populateAnnotationElement(element); + if (GeneratorUtils.getInstance().shouldReplacedByBalType(annotationElement.getDataType())) { + annotationElement.setDataType(GeneratorUtils.getInstance().resolveDataType(annotationElement.getDataType())); + } + annotation.addElement(annotationElement); + context.addElement(element); + } + } + getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(contextName.toLowerCase(), context); + DataTypesRegistry.getInstance().addDataType(context.getName()); + } + } + } + } + } + } + + @Override + public void populateExtensionResourceMap(String identifier, FHIRDataTypeDef extensionDef) { + Set extensionContext = new HashSet<>(); + + /// Retrieve context of extensions + FHIRR5DataTypeDef r5ExtensionDefinition = (FHIRR5DataTypeDef) extensionDef; + for (StructureDefinition.StructureDefinitionContextComponent contextComponent : r5ExtensionDefinition.getDefinition().getContext()) { + extensionContext.add(contextComponent.getExpression()); + } + getExtensionTemplateContext().getExtensionResources().putIfAbsent(identifier, extensionContext); + } +} \ No newline at end of file diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5PackageContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5PackageContextGenerator.java index b561699..07d31bc 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5PackageContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5PackageContextGenerator.java @@ -50,10 +50,18 @@ public R5PackageContextGenerator(BallerinaPackageGenToolConfig config, Map generatorProperties) throws CodeGenException { + BallerinaPackageGenToolConfig toolConfig = (BallerinaPackageGenToolConfig) generatorProperties.get("toolConfig"); + String packagePath = this.getTargetDir() + File.separator + toolConfig.getPackageConfig().getName(); + + ExtensionTemplateContext extensionTemplateContext = (ExtensionTemplateContext) generatorProperties.get("extensionContext"); + if (extensionTemplateContext == null) { + throw new CodeGenException("Extension context is not available."); + } + + extensionTemplateContext = GeneratorUtils.sanitizeExtensionTemplateContext(extensionTemplateContext); + + PackageTemplateContext packageTemplateContext = (PackageTemplateContext) generatorProperties.get("packageContext"); + List importList = Collections.singletonList(packageTemplateContext.getBasePackageName()); + TemplateContext templateContext = this.getNewTemplateContext(); + templateContext.setProperty("util", GeneratorUtils.getInstance()); + templateContext.setProperty("licenseYear", ToolConstants.LICENSE_YEAR); + templateContext.setProperty("extensionDataTypeContext", extensionTemplateContext.getExtensionDatatypes()); + templateContext.setProperty("extensionSliceContext", extensionTemplateContext.getExtensionSlices()); + templateContext.setProperty("imports", importList); + + if (!extensionTemplateContext.getExtensionDatatypes().isEmpty() || + !extensionTemplateContext.getExtensionResources().isEmpty()) { + String filePath = CommonUtil.generateFilePath(packagePath, "", "extensions.bal"); + this.getTemplateEngine().generateOutputAsFile(ToolConstants.TEMPLATE_PATH + + ToolConstants.RESOURCE_PATH_SEPERATOR + "extensions.vm", templateContext, "", filePath); + } + } +} diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/PackageTemplateGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/PackageTemplateGenerator.java index 3c5800c..7c74b07 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/PackageTemplateGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/PackageTemplateGenerator.java @@ -55,12 +55,15 @@ public class PackageTemplateGenerator extends AbstractFHIRTemplateGenerator { public PackageTemplateGenerator(String targetDir) throws CodeGenException { super(targetDir); LOG.debug("Package Template Generator Initiated"); - ResourceTemplateGenerator resourceTemplateGenerator = - new ResourceTemplateGenerator(targetDir); + ResourceTemplateGenerator resourceTemplateGenerator = new ResourceTemplateGenerator(targetDir); this.setChildTemplateGenerator(resourceTemplateGenerator); - DatatypeTemplateGenerator datatypeTemplateGenerator = - new DatatypeTemplateGenerator(targetDir); + + DatatypeTemplateGenerator datatypeTemplateGenerator = new DatatypeTemplateGenerator(targetDir); resourceTemplateGenerator.setChildTemplateGenerator(datatypeTemplateGenerator); + + ExtensionTemplateGenerator extensionTemplateGenerator = new ExtensionTemplateGenerator(targetDir); + datatypeTemplateGenerator.setChildTemplateGenerator(extensionTemplateGenerator); + LOG.debug("Package Template Generator Ended"); } @@ -136,25 +139,23 @@ private void generatePackageEssentials(BallerinaPackageGenToolConfig toolConfig) String packagePath = (String) this.packageProperties.get("packagePath"); String filePath = CommonUtil.generateFilePath(packagePath, "Ballerina" + ToolConstants.TOML_EXTENSION, ""); - if(toolConfig.getPackageConfig().getFhirVersion().equals("r4")){ + if (toolConfig.getPackageConfig().getFhirVersion().equals("r4")) { this.getTemplateEngine().generateOutputAsFile( ToolConstants.TEMPLATE_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + ToolConstants.TEMPLATE_VERSION_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + "r4" + ToolConstants.RESOURCE_PATH_SEPERATOR + "r4_ballerina_toml.vm", this.createTemplateContextForBallerinaToml(toolConfig), "", filePath); - } - else if (toolConfig.getPackageConfig().getFhirVersion().equals("r5")) { + } else if (toolConfig.getPackageConfig().getFhirVersion().equals("r5")) { this.getTemplateEngine().generateOutputAsFile( ToolConstants.TEMPLATE_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + ToolConstants.TEMPLATE_VERSION_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + "r5" + ToolConstants.RESOURCE_PATH_SEPERATOR + "r5_ballerina_toml.vm", this.createTemplateContextForBallerinaToml(toolConfig), "", filePath); } filePath = CommonUtil.generateFilePath(packagePath, "Package" + ToolConstants.MD_EXTENSION, ""); - if(toolConfig.getPackageConfig().getFhirVersion().equals("r4")){ + if (toolConfig.getPackageConfig().getFhirVersion().equals("r4")) { this.getTemplateEngine().generateOutputAsFile( ToolConstants.TEMPLATE_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + ToolConstants.TEMPLATE_VERSION_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + "r4" + ToolConstants.RESOURCE_PATH_SEPERATOR + "r4_package.vm", this.createTemplateContextForPackageMD(toolConfig), "", filePath); - } - else if (toolConfig.getPackageConfig().getFhirVersion().equals("r5")) { + } else if (toolConfig.getPackageConfig().getFhirVersion().equals("r5")) { this.getTemplateEngine().generateOutputAsFile( ToolConstants.TEMPLATE_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + ToolConstants.TEMPLATE_VERSION_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + "r5" + ToolConstants.RESOURCE_PATH_SEPERATOR + "r5_package.vm", this.createTemplateContextForPackageMD(toolConfig), "", filePath); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java index 4a8cf97..1560d11 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java @@ -23,8 +23,10 @@ import org.wso2.healthcare.codegen.tool.framework.commons.core.TemplateContext; import org.wso2.healthcare.codegen.tool.framework.commons.core.ToolContext; import org.wso2.healthcare.codegen.tool.framework.commons.exception.CodeGenException; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.DataTypesRegistry; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.ToolConstants; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.config.BallerinaPackageGenToolConfig; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ExtensionTemplateContext; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DataTypeProfile; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.Element; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.PackageTemplateContext; @@ -34,7 +36,14 @@ import org.wso2.healthcare.codegen.tool.framework.fhir.core.AbstractFHIRTemplateGenerator; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Iterator; +import java.util.HashSet; /** * Generator class for resource related template context @@ -44,6 +53,7 @@ public class ResourceTemplateGenerator extends AbstractFHIRTemplateGenerator { private static final Log LOG = LogFactory.getLog(ResourceTemplateGenerator.class); private final Map resourceProperties = new HashMap<>(); private PackageTemplateContext packageTemplateContext; + private ExtensionTemplateContext extensionTemplateContext; private List resourceTemplateContexts; public ResourceTemplateGenerator(String targetDir) throws CodeGenException { @@ -55,6 +65,7 @@ public ResourceTemplateGenerator(String targetDir) throws CodeGenException { public void generate(ToolContext toolContext, Map generatorProperties) throws CodeGenException { LOG.debug("Started: Resource Templates Generation"); this.packageTemplateContext = (PackageTemplateContext) generatorProperties.get("packageContext"); + this.extensionTemplateContext = (ExtensionTemplateContext) generatorProperties.get("extensionContext"); BallerinaPackageGenToolConfig toolConfig = (BallerinaPackageGenToolConfig) generatorProperties.get("toolConfig"); String packagePath = this.getTargetDir() + File.separator + toolConfig.getPackageConfig().getName(); @@ -97,6 +108,9 @@ private void generateFHIRResources() if (resourceTemplateContext.getResourceType().equals("Bundle")) continue; + Map> resourceExtensionsMap = getExtensionContext(resourceTemplateContext); + resourceTemplateContext.setResourceExtensions(resourceExtensionsMap); + String filePath = CommonUtil.generateFilePath(packagePath, "resource_" + CommonUtil.camelToSnake(resourceTemplateContext.getResourceDefinitionAnnotation().getName()) + ToolConstants.BAL_EXTENSION, ""); @@ -133,6 +147,7 @@ private TemplateContext createTemplateContextForResourceSkeletons(ResourceTempla templateContext.setProperty("extendedElements", resourceTemplateContext.getExtendedElements()); templateContext.setProperty("INT_MAX", Integer.MAX_VALUE); templateContext.setProperty("dataTypes", packageContext.getDataTypesRegistry()); + templateContext.setProperty("resourceExtensions", resourceTemplateContext.getResourceExtensions()); templateContext.setProperty("isBasePackage", this.resourceProperties.get("isBasePackage")); templateContext.setProperty("basePackageIdentifier", this.resourceProperties.get("basePackageIdentifier")); @@ -182,4 +197,32 @@ private TemplateContext createTemplateContextForResourceSkeletons(ResourceTempla templateContext.setProperty("imports", resourceDependencies); return templateContext; } + + private Map> getExtensionContext(ResourceTemplateContext resourceTemplateContext) { + Map> igExtensions = extensionTemplateContext.getExtensionResources(); + + Map> resourceExtensionsMap = new HashMap<>(); + Set resourceExtensions = new TreeSet<>(); + String resourceExtensionsPackName = resourceTemplateContext.getResourceName() + "Extensions"; + + for (Map.Entry> extensionResourceEntry : igExtensions.entrySet()) { + if (extensionResourceEntry.getValue().contains(resourceTemplateContext.getResourceType())) { + String extensionName = extensionResourceEntry.getKey(); + resourceExtensions.add("Extension"); + resourceExtensions.add(extensionName); + } + } + + if (!resourceExtensions.isEmpty()) { + resourceExtensionsMap.put(resourceExtensionsPackName, resourceExtensions); + + // todo: Uncommenting these lines will bind the extensions to resources. + // DataTypesRegistry.getInstance().addDataType(resourceExtensionsPackName); + // resourceTemplateContext.getResourceDefinitionAnnotation().getElements().get("extension").setDataType(resourceExtensionsPackName); + // resourceTemplateContext.getResourceElements().get("extension").setDataType(resourceExtensionsPackName); + // resourceTemplateContext.getResourceElements().get("extension").getProfiles().get("Extension").setProfileType(resourceExtensionsPackName); + } + + return resourceExtensionsMap; + } } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java index 70ce79e..e6cb9f8 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java @@ -28,11 +28,14 @@ import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.Element; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ExtendedElement; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.SearchParameter; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ExtensionTemplateContext; +import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.DatatypeTemplateContext; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; public class GeneratorUtils { @@ -658,4 +661,58 @@ public static boolean checkForInternationalImports(Element element) { return false; } + + /** + * Remove duplicate datatypes and slices from the extension context. + * The edge case was first detected by the davincipas profile. + * + * @param extensionTemplateContext The extension template context to be sanitized. + * @return The sanitized extension template context. + * + */ + public static ExtensionTemplateContext sanitizeExtensionTemplateContext(ExtensionTemplateContext extensionTemplateContext) { + Map extensionDatatypes = extensionTemplateContext.getExtensionDatatypes(); + Map> extensionSlices = extensionTemplateContext.getExtensionSlices(); + + // Keep track of element names and their counts + Map elementNameCountMap = new HashMap<>(); + List toBeRemovedDatatypes = new ArrayList<>(); + List toBeRemovedSlices = new ArrayList<>(); + + if (extensionDatatypes != null) { + for (Map.Entry entry : extensionDatatypes.entrySet()) { + if (!elementNameCountMap.containsKey(entry.getValue().getName())) { + elementNameCountMap.putIfAbsent(entry.getValue().getName(), 1); + } else { + elementNameCountMap.put(entry.getValue().getName(), elementNameCountMap.get(entry.getValue().getName()) + 1); + } + } + + // Identify duplicate datatypes and slices to be removed + for (Map.Entry entry : elementNameCountMap.entrySet()) { + if (entry.getValue() > 1) { + for (Map.Entry datatypeEntry : extensionDatatypes.entrySet()) { + if (datatypeEntry.getValue().getName().equals(entry.getKey())) { + if (datatypeEntry.getValue().getElements().isEmpty()) { + toBeRemovedDatatypes.add(datatypeEntry.getKey()); + toBeRemovedSlices.add(entry.getKey() + "Extensions"); + } + } + } + } + } + + // Remove identified duplicate datatypes + for (String datatypeKey : toBeRemovedDatatypes) { + extensionDatatypes.remove(datatypeKey); + } + + // Remove identified duplicate slices + for (String sliceKey : toBeRemovedSlices) { + extensionSlices.remove(sliceKey); + } + } + + return extensionTemplateContext; + } } diff --git a/native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm b/native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm new file mode 100644 index 0000000..eae22bd --- /dev/null +++ b/native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm @@ -0,0 +1,88 @@ +// Copyright (c) ${licenseYear}, WSO2 LLC. (http://www.wso2.com). + +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// AUTO-GENERATED FILE. +// This file is auto-generated by Ballerina. + +#set($tab=" ") +#foreach($import in $imports) +#if($import) +import $import; +#end +#end + +#foreach($extensionSlice in $extensionSliceContext.entrySet()) +#set($i=0) +#set($length = $extensionSlice.getValue().size()) +public type $extensionSlice.getKey() (#foreach($childExtension in $extensionSlice.getValue())$childExtension #set($i=$i+1)#if($i<$length)| #else)#end#end; +#end + +#foreach ($mapEntry in $extensionDataTypeContext.entrySet()) +@${util.getBasePackageIdentifier()}DataTypeDefinition { + name: "${mapEntry.value.annotation.name}", + baseType: (), + #set($elementEntries = $mapEntry.value.annotation.elements.entrySet()) + #set($i = 0) + #set($len = $elementEntries.size()) + elements: { + #foreach ($elementMapEntry in $elementEntries) + #if($elementMapEntry) + "${elementMapEntry.value.name}": { + name: "$util.resolveSpecialCharacters(${elementMapEntry.value.name})", + dataType: ${elementMapEntry.value.getDataTypeWithImportPrefix()}, + min: ${elementMapEntry.value.min}, + max: ${elementMapEntry.value.max}, + isArray: ${elementMapEntry.value.isArray()}, + description: "${elementMapEntry.value.description}", + path: "${elementMapEntry.value.path}" + }#if($i < $len - 1),#end + #set($i = $i + 1) + + #end + #end + }, + serializers: { + 'xml: ${util.getBasePackageIdentifier()}complexDataTypeXMLSerializer, + 'json: ${util.getBasePackageIdentifier()}complexDataTypeJsonSerializer + } +} + +public type ${mapEntry.value.name} record {| + #if($util.isTypeInclusion($mapEntry.value.baseDataType)) + *$util.getTypeWithImport($mapEntry.value.baseDataType); + #end + + #foreach ($elementMapEntry in ${mapEntry.value.elements.entrySet()}) + #if($elementMapEntry && $elementMapEntry.value.max != 0) + $tab#if($elementMapEntry.value.hasFixedValue())#foreach($fixedValue in $elementMapEntry.value.getFixedValue())"$fixedValue"#end#else${util.getTypeWithImport($elementMapEntry.value.dataType)}#end#if($elementMapEntry.value.isArray())[]#end $util.resolveSpecialCharacters(${elementMapEntry.value.name})#if(!$elementMapEntry.value.isRequired())?#end#if($elementMapEntry.value.hasFixedValue())#foreach($fixedValue in $elementMapEntry.value.getFixedValue()) = "$fixedValue"#end#end; + #end + #end +|}; + +#foreach ($elementMapEntry in ${mapEntry.value.extendedElements.entrySet()}) +#if($elementMapEntry.value.getBalDataType().getType() == 'enum') +public enum $elementMapEntry.value.typeName { + #set($childEntries = $elementMapEntry.value.elements.entrySet()) + #set($i = 0) + #set($len = $childEntries.size()) + #foreach ($childElementMapEntry in $childEntries) + CODE_${elementMapEntry.value.typeName.toUpperCase()}_$util.resolveSpecialCharacters($childElementMapEntry.key.toUpperCase()).toUpperCase() = "$childElementMapEntry.key"#if($i < $len - 1),#end + #set($i = $i + 1) + #end +}; + #end + #end +#end \ No newline at end of file diff --git a/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm b/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm index 323c94b..d91b234 100644 --- a/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm +++ b/native/fhir-to-bal-lib/src/main/resources/templates/fhir_resource.vm @@ -28,6 +28,12 @@ import $import.toString(); public const string PROFILE_BASE_${util.resolveSpecialCharacters($resourceName.toUpperCase())} = "${profile}"; public const RESOURCE_NAME_${util.resolveSpecialCharacters($resourceName.toUpperCase())} = "${resourceType}"; +#foreach($resourceExtension in $resourceExtensions.entrySet()) +#set($i=0) +#set($length = $resourceExtension.getValue().size()) +public type $resourceExtension.getKey() (#foreach($extension in $resourceExtension.getValue())#if(!$dataTypes.contains($extension))${importIdentifier}#end$extension #set($i=$i+1)#if($i<$length)| #end#end); +#end + # FHIR $resourceName resource record. # # + resourceType - The type of the resource describes diff --git a/native/health-cli/src/test/resources/profiles.EuropeBase/StructureDefinition-eu-core-ext-patient-religion.json b/native/health-cli/src/test/resources/profiles.EuropeBase/StructureDefinition-eu-core-ext-patient-religion.json new file mode 100644 index 0000000..29c8a61 --- /dev/null +++ b/native/health-cli/src/test/resources/profiles.EuropeBase/StructureDefinition-eu-core-ext-patient-religion.json @@ -0,0 +1,903 @@ +{ + "resourceType": "StructureDefinition", + "id": "eu-core-ext-patient-religion", + "text": { + "status": "extensions", + "div": "

Generated Narrative: StructureDefinition eu-core-ext-patient-religion

\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n
NameFlagsCard.TypeDescription & Constraints\"doco\"
\".\"\".\" Extension 0..*ExtensionPatient Religion
\".\"\".\"\".\" Slices for extension Content/Rules for all slices
\".\"\".\"\".\"\".\" extension:code 0..1ExtensionReligion code of the Patient
\".\"\".\"\".\"\".\"\".\" extension 0..0
\".\"\".\"\".\"\".\"\".\" url 1..1uri"code"
\".\"\".\"\".\"\".\"\".\" value[x] 0..1CodeableConceptValue of extension
Binding: https://termgit.elga.gv.eu/ValueSet/elga-religiousaffiliation (extensible)
\".\"\".\"\".\"\".\" extension:period 0..1ExtensionTime period of the Religion
\".\"\".\"\".\"\".\"\".\" extension 0..0
\".\"\".\"\".\"\".\"\".\" url 1..1uri"period"
\".\"\".\"\".\"\".\"\".\" value[x] 1..1PeriodValue of extension
\".\"\".\"\".\" url 1..1uri"http://hl7.eu/fhir/HL7ATCoreProfiles/5.0.0/StructureDefinition/eu-core-ext-patient-religion"
\".\"\".\"\".\" value[x] 0..0

\"doco\" Documentation for this format
" + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-type-characteristics", + "valueCode": "can-bind" + } + ], + "url": "http://hl7.eu/fhir/HL7ATCoreProfiles/5.0.0/StructureDefinition/eu-core-ext-patient-religion", + "version": "2.0.0", + "name": "PatientReligion", + "title": "Patient Religion", + "status": "active", + "date": "2025-01-24T14:21:13+00:00", + "publisher": "HL7® Austria, TC FHIR®", + "contact": [ + { + "name": "HL7® Austria, TC FHIR®", + "telecom": [ + { + "system": "url", + "value": "https://hl7.eu/technische-komitees/tc-fhir/" + } + ] + }, + { + "name": "Technical Committee for FHIR® eu HL7® Austria", + "telecom": [ + { + "system": "email", + "value": "mailto:tc-fhir@hl7.eu" + } + ] + } + ], + "description": "HL7® Austria FHIR® Core Extension for the religion (registered in Austria) of a patient.\r\nThe extension is used to encode the religious confession of a patient (only confessions registered in Austria). Furthermore, it uses the official [HL7 AT CodeSystem](https://termpub.gesundheit.gv.eu:443/TermBrowser/gui/main/main.zul?loadType=CodeSystem&loadName=HL7 AT ReligionAustria) for religion and is therefore aligned with the ELGA ValueSet, respectively.", + "fhirVersion": "5.0.0", + "mapping": [ + { + "identity": "rim", + "uri": "http://hl7.org/v3", + "name": "RIM Mapping" + } + ], + "kind": "complex-type", + "abstract": false, + "context": [ + { + "type": "element", + "expression": "Patient" + }, + { + "type": "element", + "expression": "http://hl7.eu/fhir/HL7ATCoreProfiles/5.0.0/StructureDefinition/eu-core-patient#Patient" + } + ], + "type": "Extension", + "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Extension", + "derivation": "constraint", + "snapshot": { + "element": [ + { + "id": "Extension", + "path": "Extension", + "short": "Patient Religion", + "definition": "HL7® Austria FHIR® Core Extension for the religion (registered in Austria) of a patient.\r\nThe extension is used to encode the religious confession of a patient (only confessions registered in Austria). Furthermore, it uses the official [HL7 AT CodeSystem](https://termpub.gesundheit.gv.eu:443/TermBrowser/gui/main/main.zul?loadType=CodeSystem&loadName=HL7 AT ReligionAustria) for religion and is therefore aligned with the ELGA ValueSet, respectively.", + "min": 0, + "max": "*", + "base": { + "path": "Extension", + "min": 0, + "max": "*" + }, + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + }, + { + "key": "ext-1", + "severity": "error", + "human": "Must have either extensions or value[x], not both", + "expression": "extension.exists() != value.exists()", + "source": "http://hl7.org/fhir/StructureDefinition/Extension" + } + ], + "isModifier": false + }, + { + "id": "Extension.id", + "path": "Extension.id", + "representation": [ + "xmlAttr" + ], + "short": "Unique id for inter-element referencing", + "definition": "Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.", + "min": 0, + "max": "1", + "base": { + "path": "Element.id", + "min": 0, + "max": "1" + }, + "type": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", + "valueUrl": "id" + } + ], + "code": "http://hl7.org/fhirpath/System.String" + } + ], + "condition": [ + "ele-1" + ], + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "n/a" + } + ] + }, + { + "id": "Extension.extension", + "path": "Extension.extension", + "slicing": { + "discriminator": [ + { + "type": "value", + "path": "url" + } + ], + "description": "Extensions are always sliced by (eu least) url", + "rules": "open" + }, + "short": "Additional content defined by implementations", + "definition": "May be used to represent additional information that is not part of the basic definition of the element. To make the use of extensions safe and managable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer can define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension.", + "comment": "There can be no stigma associated with the use of extensions by any application, project, or standard - regardless of the institution or jurisdiction that uses or defines the extensions. The use of extensions is what allows the FHIR specification to retain a core level of simplicity for everyone.", + "alias": [ + "extensions", + "user content" + ], + "min": 0, + "max": "*", + "base": { + "path": "Element.extension", + "min": 0, + "max": "*" + }, + "type": [ + { + "code": "Extension" + } + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + }, + { + "key": "ext-1", + "severity": "error", + "human": "Must have either extensions or value[x], not both", + "expression": "extension.exists() != value.exists()", + "source": "http://hl7.org/fhir/StructureDefinition/Extension" + } + ], + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "n/a" + } + ] + }, + { + "id": "Extension.extension:code", + "path": "Extension.extension", + "sliceName": "code", + "short": "Religion code of the Patient", + "definition": "An Extension", + "min": 0, + "max": "1", + "base": { + "path": "Element.extension", + "min": 0, + "max": "*" + }, + "type": [ + { + "code": "Extension" + } + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + }, + { + "key": "ext-1", + "severity": "error", + "human": "Must have either extensions or value[x], not both", + "expression": "extension.exists() != value.exists()", + "source": "http://hl7.org/fhir/StructureDefinition/Extension" + } + ], + "isModifier": false, + "isSummary": false + }, + { + "id": "Extension.extension:code.id", + "path": "Extension.extension.id", + "representation": [ + "xmlAttr" + ], + "short": "Unique id for inter-element referencing", + "definition": "Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.", + "min": 0, + "max": "1", + "base": { + "path": "Element.id", + "min": 0, + "max": "1" + }, + "type": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", + "valueUrl": "id" + } + ], + "code": "http://hl7.org/fhirpath/System.String" + } + ], + "condition": [ + "ele-1" + ], + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "n/a" + } + ] + }, + { + "id": "Extension.extension:code.extension", + "path": "Extension.extension.extension", + "slicing": { + "discriminator": [ + { + "type": "value", + "path": "url" + } + ], + "description": "Extensions are always sliced by (at least) url", + "rules": "open" + }, + "short": "Extension", + "definition": "An Extension", + "min": 0, + "max": "0", + "base": { + "path": "Element.extension", + "min": 0, + "max": "*" + }, + "type": [ + { + "code": "Extension" + } + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + }, + { + "key": "ext-1", + "severity": "error", + "human": "Must have either extensions or value[x], not both", + "expression": "extension.exists() != value.exists()", + "source": "http://hl7.org/fhir/StructureDefinition/Extension" + } + ], + "isModifier": false, + "isSummary": false + }, + { + "id": "Extension.extension:code.url", + "path": "Extension.extension.url", + "representation": [ + "xmlAttr" + ], + "short": "identifies the meaning of the extension", + "definition": "Source of the definition for the extension code - a logical name or a URL.", + "comment": "The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.", + "min": 1, + "max": "1", + "base": { + "path": "Extension.url", + "min": 1, + "max": "1" + }, + "type": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", + "valueUrl": "uri" + } + ], + "code": "http://hl7.org/fhirpath/System.String" + } + ], + "fixedUri": "code", + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "N/A" + } + ] + }, + { + "id": "Extension.extension:code.value[x]", + "path": "Extension.extension.value[x]", + "short": "Value of extension", + "definition": "Value of extension - must be one of a constrained set of the data types (see [Extensibility](http://hl7.org/fhir/R5/extensibility.html) for a list).", + "min": 0, + "max": "1", + "base": { + "path": "Extension.value[x]", + "min": 0, + "max": "1" + }, + "type": [ + { + "code": "CodeableConcept" + } + ], + "condition": [ + "ext-1" + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + } + ], + "isModifier": false, + "isSummary": false, + "binding": { + "strength": "extensible", + "valueSet": "https://termgit.elga.gv.eu/ValueSet/elga-religiousaffiliation" + }, + "mapping": [ + { + "identity": "rim", + "map": "N/A" + } + ] + }, + { + "id": "Extension.extension:period", + "path": "Extension.extension", + "sliceName": "period", + "short": "Time period of the Religion", + "definition": "An Extension", + "min": 0, + "max": "1", + "base": { + "path": "Element.extension", + "min": 0, + "max": "*" + }, + "type": [ + { + "code": "Extension" + } + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + }, + { + "key": "ext-1", + "severity": "error", + "human": "Must have either extensions or value[x], not both", + "expression": "extension.exists() != value.exists()", + "source": "http://hl7.org/fhir/StructureDefinition/Extension" + } + ], + "isModifier": false, + "isSummary": false + }, + { + "id": "Extension.extension:period.id", + "path": "Extension.extension.id", + "representation": [ + "xmlAttr" + ], + "short": "Unique id for inter-element referencing", + "definition": "Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.", + "min": 0, + "max": "1", + "base": { + "path": "Element.id", + "min": 0, + "max": "1" + }, + "type": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", + "valueUrl": "id" + } + ], + "code": "http://hl7.org/fhirpath/System.String" + } + ], + "condition": [ + "ele-1" + ], + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "n/a" + } + ] + }, + { + "id": "Extension.extension:period.extension", + "path": "Extension.extension.extension", + "slicing": { + "discriminator": [ + { + "type": "value", + "path": "url" + } + ], + "description": "Extensions are always sliced by (at least) url", + "rules": "open" + }, + "short": "Extension", + "definition": "An Extension", + "min": 0, + "max": "0", + "base": { + "path": "Element.extension", + "min": 0, + "max": "*" + }, + "type": [ + { + "code": "Extension" + } + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + }, + { + "key": "ext-1", + "severity": "error", + "human": "Must have either extensions or value[x], not both", + "expression": "extension.exists() != value.exists()", + "source": "http://hl7.org/fhir/StructureDefinition/Extension" + } + ], + "isModifier": false, + "isSummary": false + }, + { + "id": "Extension.extension:period.url", + "path": "Extension.extension.url", + "representation": [ + "xmlAttr" + ], + "short": "identifies the meaning of the extension", + "definition": "Source of the definition for the extension code - a logical name or a URL.", + "comment": "The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.", + "min": 1, + "max": "1", + "base": { + "path": "Extension.url", + "min": 1, + "max": "1" + }, + "type": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", + "valueUrl": "uri" + } + ], + "code": "http://hl7.org/fhirpath/System.String" + } + ], + "fixedUri": "period", + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "N/A" + } + ] + }, + { + "id": "Extension.extension:period.value[x]", + "path": "Extension.extension.value[x]", + "short": "Value of extension", + "definition": "Value of extension - must be one of a constrained set of the data types (see [Extensibility](http://hl7.org/fhir/R5/extensibility.html) for a list).", + "min": 1, + "max": "1", + "base": { + "path": "Extension.value[x]", + "min": 0, + "max": "1" + }, + "type": [ + { + "code": "Period" + } + ], + "condition": [ + "ext-1" + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + } + ], + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "N/A" + } + ] + }, + { + "id": "Extension.url", + "path": "Extension.url", + "representation": [ + "xmlAttr" + ], + "short": "identifies the meaning of the extension", + "definition": "Source of the definition for the extension code - a logical name or a URL.", + "comment": "The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.", + "min": 1, + "max": "1", + "base": { + "path": "Extension.url", + "min": 1, + "max": "1" + }, + "type": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type", + "valueUrl": "uri" + } + ], + "code": "http://hl7.org/fhirpath/System.String" + } + ], + "fixedUri": "http://hl7.eu/fhir/HL7ATCoreProfiles/5.0.0/StructureDefinition/eu-core-ext-patient-religion", + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "N/A" + } + ] + }, + { + "id": "Extension.value[x]", + "path": "Extension.value[x]", + "short": "Value of extension", + "definition": "Value of extension - must be one of a constrained set of the data types (see [Extensibility](http://hl7.org/fhir/R5/extensibility.html) for a list).", + "min": 0, + "max": "0", + "base": { + "path": "Extension.value[x]", + "min": 0, + "max": "1" + }, + "type": [ + { + "code": "base64Binary" + }, + { + "code": "boolean" + }, + { + "code": "canonical" + }, + { + "code": "code" + }, + { + "code": "date" + }, + { + "code": "dateTime" + }, + { + "code": "decimal" + }, + { + "code": "id" + }, + { + "code": "instant" + }, + { + "code": "integer" + }, + { + "code": "integer64" + }, + { + "code": "markdown" + }, + { + "code": "oid" + }, + { + "code": "positiveInt" + }, + { + "code": "string" + }, + { + "code": "time" + }, + { + "code": "unsignedInt" + }, + { + "code": "uri" + }, + { + "code": "url" + }, + { + "code": "uuid" + }, + { + "code": "Address" + }, + { + "code": "Age" + }, + { + "code": "Annotation" + }, + { + "code": "Attachment" + }, + { + "code": "CodeableConcept" + }, + { + "code": "CodeableReference" + }, + { + "code": "Coding" + }, + { + "code": "ContactPoint" + }, + { + "code": "Count" + }, + { + "code": "Distance" + }, + { + "code": "Duration" + }, + { + "code": "HumanName" + }, + { + "code": "Identifier" + }, + { + "code": "Money" + }, + { + "code": "Period" + }, + { + "code": "Quantity" + }, + { + "code": "Range" + }, + { + "code": "Ratio" + }, + { + "code": "RatioRange" + }, + { + "code": "Reference" + }, + { + "code": "SampledData" + }, + { + "code": "Signature" + }, + { + "code": "Timing" + }, + { + "code": "ContactDetail" + }, + { + "code": "DataRequirement" + }, + { + "code": "Expression" + }, + { + "code": "ParameterDefinition" + }, + { + "code": "RelatedArtifact" + }, + { + "code": "TriggerDefinition" + }, + { + "code": "UsageContext" + }, + { + "code": "Availability" + }, + { + "code": "ExtendedContactDetail" + }, + { + "code": "Dosage" + }, + { + "code": "Meta" + } + ], + "condition": [ + "ext-1" + ], + "constraint": [ + { + "key": "ele-1", + "severity": "error", + "human": "All FHIR elements must have a @value or children", + "expression": "hasValue() or (children().count() > id.count())", + "source": "http://hl7.org/fhir/StructureDefinition/Element" + } + ], + "isModifier": false, + "isSummary": false, + "mapping": [ + { + "identity": "rim", + "map": "N/A" + } + ] + } + ] + }, + "differential": { + "element": [ + { + "id": "Extension", + "path": "Extension", + "short": "Patient Religion", + "definition": "HL7® Austria FHIR® Core Extension for the religion (registered in Austria) of a patient.\r\nThe extension is used to encode the religious confession of a patient (only confessions registered in Austria). Furthermore, it uses the official [HL7 AT CodeSystem](https://termpub.gesundheit.gv.eu:443/TermBrowser/gui/main/main.zul?loadType=CodeSystem&loadName=HL7 AT ReligionAustria) for religion and is therefore aligned with the ELGA ValueSet, respectively." + }, + { + "id": "Extension.extension:code", + "path": "Extension.extension", + "sliceName": "code", + "short": "Religion code of the Patient", + "min": 0, + "max": "1" + }, + { + "id": "Extension.extension:code.extension", + "path": "Extension.extension.extension", + "max": "0" + }, + { + "id": "Extension.extension:code.url", + "path": "Extension.extension.url", + "fixedUri": "code" + }, + { + "id": "Extension.extension:code.value[x]", + "path": "Extension.extension.value[x]", + "type": [ + { + "code": "CodeableConcept" + } + ], + "binding": { + "strength": "extensible", + "valueSet": "https://termgit.elga.gv.eu/ValueSet/elga-religiousaffiliation" + } + }, + { + "id": "Extension.extension:period", + "path": "Extension.extension", + "sliceName": "period", + "short": "Time period of the Religion", + "min": 0, + "max": "1" + }, + { + "id": "Extension.extension:period.extension", + "path": "Extension.extension.extension", + "max": "0" + }, + { + "id": "Extension.extension:period.url", + "path": "Extension.extension.url", + "fixedUri": "period" + }, + { + "id": "Extension.extension:period.value[x]", + "path": "Extension.extension.value[x]", + "min": 1, + "type": [ + { + "code": "Period" + } + ] + }, + { + "id": "Extension.url", + "path": "Extension.url", + "fixedUri": "http://hl7.eu/fhir/HL7ATCoreProfiles/5.0.0/StructureDefinition/eu-core-ext-patient-religion" + }, + { + "id": "Extension.value[x]", + "path": "Extension.value[x]", + "max": "0" + } + ] + } +} \ No newline at end of file From 84f7a4f8d723b8ed169017f3445cc12c5bbb981d Mon Sep 17 00:00:00 2001 From: "A.M.S.I Attanayake" Date: Fri, 19 Sep 2025 10:34:40 +0530 Subject: [PATCH 4/6] fix PR review comments --- .../tool/model/ExtensionTemplateContext.java | 36 +++++++++---------- .../AbstractExtensionContextGenerator.java | 10 ++++-- .../r4/R4ExtensionContextGenerator.java | 9 ++--- .../r5/R5ExtensionContextGenerator.java | 19 +++++++--- .../ExtensionTemplateGenerator.java | 8 ++--- .../ResourceTemplateGenerator.java | 3 +- .../packagegen/tool/utils/GeneratorUtils.java | 4 +-- .../main/resources/templates/extensions.vm | 2 +- 8 files changed, 53 insertions(+), 38 deletions(-) diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java index 08176ff..74e7e16 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/model/ExtensionTemplateContext.java @@ -24,37 +24,37 @@ import java.util.HashMap; public class ExtensionTemplateContext { - private Map extensionDatatypes; - private Map> extensionSlices; - private Map> extensionResources; + private Map extendedDatatypes; + private Map> extendedSlices; + private Map> extendedResources; public ExtensionTemplateContext() { - extensionDatatypes = new TreeMap<>(); - extensionSlices = new HashMap<>(); - extensionResources = new HashMap<>(); + extendedDatatypes = new TreeMap<>(); + extendedSlices = new HashMap<>(); + extendedResources = new HashMap<>(); } - public Map getExtensionDatatypes() { - return extensionDatatypes; + public Map getExtendedDatatypes() { + return extendedDatatypes; } - public void setExtensionDatatypes(Map extensionDatatypes) { - this.extensionDatatypes = extensionDatatypes; + public void setExtendedDatatypes(Map extendedDatatypes) { + this.extendedDatatypes = extendedDatatypes; } - public Map> getExtensionSlices() { - return extensionSlices; + public Map> getExtendedSlices() { + return extendedSlices; } - public void setExtensionSlices(Map> extensionSlices) { - this.extensionSlices = extensionSlices; + public void setExtendedSlices(Map> extendedSlices) { + this.extendedSlices = extendedSlices; } - public Map> getExtensionResources() { - return extensionResources; + public Map> getExtendedResources() { + return extendedResources; } - public void setExtensionResources(Map> extensionResources) { - this.extensionResources = extensionResources; + public void setExtendedResources(Map> extendedResources) { + this.extendedResources = extendedResources; } } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java index ac64727..b837cf5 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/AbstractExtensionContextGenerator.java @@ -31,6 +31,12 @@ import java.util.HashSet; import java.util.HashMap; +/** + * + * Abstract class for extensions generator context. + * Extended by the version specific extension context generators. + * Define abstract methods to be implemented by the child classes in extension generation. + */ public abstract class AbstractExtensionContextGenerator { private static final Log LOG = LogFactory.getLog(AbstractExtensionContextGenerator.class); private final Map extensionDefnMap; @@ -70,7 +76,7 @@ private void populateExtensionTemplateContext() { protected void populateExtensionSliceMap() { Map> childExtensionMap = new HashMap<>(); Set searchKeys = new HashSet<>(); - Map extensionDatatypeMap = extensionTemplateContext.getExtensionDatatypes(); + Map extensionDatatypeMap = extensionTemplateContext.getExtendedDatatypes(); for (Map.Entry contextEntry : extensionDatatypeMap.entrySet()) { if (contextEntry.getKey().contains("http://")) { @@ -94,6 +100,6 @@ protected void populateExtensionSliceMap() { } childExtensionMap.entrySet().removeIf(entry -> entry.getValue().isEmpty()); - extensionTemplateContext.setExtensionSlices(childExtensionMap); + extensionTemplateContext.setExtendedSlices(childExtensionMap); } } diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java index 7cd13f4..99a9ec1 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java @@ -83,6 +83,7 @@ public void populateBaseExtensionContext() { element.setDataType(value); } + /// Read the datatype of the element from the Type.Ref component String typeCode = elementDefinition.getType().get(0).getCode(); if (typeCode == null) { // Give a type for PrimitiveType Extensions marked with "_" @@ -119,7 +120,7 @@ public void populateBaseExtensionContext() { } } populateExtensionResourceMap(elementIdentifier, extensionDefEntry.getValue()); - getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(r4ExtensionDefn.getDefinition().getUrl(), context); + getExtensionTemplateContext().getExtendedDatatypes().putIfAbsent(r4ExtensionDefn.getDefinition().getUrl(), context); DataTypesRegistry.getInstance().addDataType(context.getName()); } } @@ -198,7 +199,7 @@ public void populateSliceExtensionContext() { context.addElement(element); } } - getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(contextName.toLowerCase(), context); + getExtensionTemplateContext().getExtendedDatatypes().putIfAbsent(contextName.toLowerCase(), context); DataTypesRegistry.getInstance().addDataType(context.getName()); } } @@ -216,6 +217,6 @@ public void populateExtensionResourceMap(String identifier, FHIRDataTypeDef exte for (StructureDefinition.StructureDefinitionContextComponent contextComponent : r4ExtensionDefinition.getDefinition().getContext()) { extensionContext.add(contextComponent.getExpression()); } - getExtensionTemplateContext().getExtensionResources().putIfAbsent(identifier, extensionContext); + getExtensionTemplateContext().getExtendedResources().putIfAbsent(identifier, extensionContext); } -} \ No newline at end of file +} diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java index 665033a..07a924e 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java @@ -119,7 +119,7 @@ public void populateBaseExtensionContext() { } } populateExtensionResourceMap(elementIdentifier, extensionDefEntry.getValue()); - getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(r5ExtensionDefn.getDefinition().getUrl(), context); + getExtensionTemplateContext().getExtendedDatatypes().putIfAbsent(r5ExtensionDefn.getDefinition().getUrl(), context); DataTypesRegistry.getInstance().addDataType(context.getName()); } } @@ -163,7 +163,16 @@ public void populateSliceExtensionContext() { element.setMin(sliceElementDefinition.getMin()); element.setArray(!"0".equals(sliceElementDefinition.getBase().getMax()) && !"1".equals(sliceElementDefinition.getBase().getMax())); - String typeCode = sliceElementDefinition.getType().get(0).getCode(); + /// Read the datatype of the element from the Type.Ref component + /// TypeCode usually have only one element. + String typeCode = elementDefinition.getType().get(0).getCode(); + if (typeCode == null) { + // Special Case: TypeCode is null + // Give a type for PrimitiveType Extensions marked with "_" + // E.g.: type [{_code:{...}}] + typeCode = "Extension"; + } + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { element.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); } else { @@ -198,7 +207,7 @@ public void populateSliceExtensionContext() { context.addElement(element); } } - getExtensionTemplateContext().getExtensionDatatypes().putIfAbsent(contextName.toLowerCase(), context); + getExtensionTemplateContext().getExtendedDatatypes().putIfAbsent(contextName.toLowerCase(), context); DataTypesRegistry.getInstance().addDataType(context.getName()); } } @@ -216,6 +225,6 @@ public void populateExtensionResourceMap(String identifier, FHIRDataTypeDef exte for (StructureDefinition.StructureDefinitionContextComponent contextComponent : r5ExtensionDefinition.getDefinition().getContext()) { extensionContext.add(contextComponent.getExpression()); } - getExtensionTemplateContext().getExtensionResources().putIfAbsent(identifier, extensionContext); + getExtensionTemplateContext().getExtendedResources().putIfAbsent(identifier, extensionContext); } -} \ No newline at end of file +} diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ExtensionTemplateGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ExtensionTemplateGenerator.java index 0dc28af..de52e12 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ExtensionTemplateGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ExtensionTemplateGenerator.java @@ -62,12 +62,12 @@ public void generate(ToolContext toolContext, Map generatorPrope TemplateContext templateContext = this.getNewTemplateContext(); templateContext.setProperty("util", GeneratorUtils.getInstance()); templateContext.setProperty("licenseYear", ToolConstants.LICENSE_YEAR); - templateContext.setProperty("extensionDataTypeContext", extensionTemplateContext.getExtensionDatatypes()); - templateContext.setProperty("extensionSliceContext", extensionTemplateContext.getExtensionSlices()); + templateContext.setProperty("extensionDataTypeContext", extensionTemplateContext.getExtendedDatatypes()); + templateContext.setProperty("extensionSliceContext", extensionTemplateContext.getExtendedSlices()); templateContext.setProperty("imports", importList); - if (!extensionTemplateContext.getExtensionDatatypes().isEmpty() || - !extensionTemplateContext.getExtensionResources().isEmpty()) { + if (!extensionTemplateContext.getExtendedDatatypes().isEmpty() || + !extensionTemplateContext.getExtendedResources().isEmpty()) { String filePath = CommonUtil.generateFilePath(packagePath, "", "extensions.bal"); this.getTemplateEngine().generateOutputAsFile(ToolConstants.TEMPLATE_PATH + ToolConstants.RESOURCE_PATH_SEPERATOR + "extensions.vm", templateContext, "", filePath); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java index 1560d11..27f48cc 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/templategen/ResourceTemplateGenerator.java @@ -23,7 +23,6 @@ import org.wso2.healthcare.codegen.tool.framework.commons.core.TemplateContext; import org.wso2.healthcare.codegen.tool.framework.commons.core.ToolContext; import org.wso2.healthcare.codegen.tool.framework.commons.exception.CodeGenException; -import org.wso2.healthcare.fhir.ballerina.packagegen.tool.DataTypesRegistry; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.ToolConstants; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.config.BallerinaPackageGenToolConfig; import org.wso2.healthcare.fhir.ballerina.packagegen.tool.model.ExtensionTemplateContext; @@ -199,7 +198,7 @@ private TemplateContext createTemplateContextForResourceSkeletons(ResourceTempla } private Map> getExtensionContext(ResourceTemplateContext resourceTemplateContext) { - Map> igExtensions = extensionTemplateContext.getExtensionResources(); + Map> igExtensions = extensionTemplateContext.getExtendedResources(); Map> resourceExtensionsMap = new HashMap<>(); Set resourceExtensions = new TreeSet<>(); diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java index e6cb9f8..586385c 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/utils/GeneratorUtils.java @@ -671,8 +671,8 @@ public static boolean checkForInternationalImports(Element element) { * */ public static ExtensionTemplateContext sanitizeExtensionTemplateContext(ExtensionTemplateContext extensionTemplateContext) { - Map extensionDatatypes = extensionTemplateContext.getExtensionDatatypes(); - Map> extensionSlices = extensionTemplateContext.getExtensionSlices(); + Map extensionDatatypes = extensionTemplateContext.getExtendedDatatypes(); + Map> extensionSlices = extensionTemplateContext.getExtendedSlices(); // Keep track of element names and their counts Map elementNameCountMap = new HashMap<>(); diff --git a/native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm b/native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm index eae22bd..72995a7 100644 --- a/native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm +++ b/native/fhir-to-bal-lib/src/main/resources/templates/extensions.vm @@ -85,4 +85,4 @@ public enum $elementMapEntry.value.typeName { }; #end #end -#end \ No newline at end of file +#end From d8813f636e0f0233ea8b6db281db6c7cd2823b8b Mon Sep 17 00:00:00 2001 From: "A.M.S.I Attanayake" Date: Thu, 25 Sep 2025 15:54:20 +0530 Subject: [PATCH 5/6] update codegen versions --- .github/workflows/cd.yml | 2 +- .github/workflows/ci.yml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 4d744df..4fcb457 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@v3 with: repository: wso2/open-healthcare-codegen-tool-framework - ref: v2.1.1 + ref: v2.1.2 path: './open-healthcare-codegen-tool-framework' - name: Setup Java and Maven diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc2b011..3cad9e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v3 with: repository: wso2/open-healthcare-codegen-tool-framework - ref: v2.1.1 + ref: v2.1.2 path: './open-healthcare-codegen-tool-framework' - name: Setup Maven diff --git a/pom.xml b/pom.xml index 5bf002f..585402b 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 21 21 UTF-8 - 2.1.1 + 2.1.2 2201.12.3 3.1.0 3.8.1 From ab0cd245a3ca26b63c01dfc8c9ddad96e997c269 Mon Sep 17 00:00:00 2001 From: "A.M.S.I Attanayake" Date: Thu, 25 Sep 2025 16:25:56 +0530 Subject: [PATCH 6/6] fix id datatype override error --- .../modelgen/versions/r4/R4ExtensionContextGenerator.java | 6 ++++++ .../modelgen/versions/r5/R5ExtensionContextGenerator.java | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java index 99a9ec1..6c2f86c 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r4/R4ExtensionContextGenerator.java @@ -89,6 +89,8 @@ public void populateBaseExtensionContext() { // Give a type for PrimitiveType Extensions marked with "_" // E.g.: type [{_code:{...}}] typeCode = "Extension"; + } else if ("id".equals(elementName)) { + typeCode = "http://hl7.org/fhirpath/System.String"; } if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { @@ -165,6 +167,10 @@ public void populateSliceExtensionContext() { element.setArray(!"0".equals(sliceElementDefinition.getBase().getMax()) && !"1".equals(sliceElementDefinition.getBase().getMax())); String typeCode = sliceElementDefinition.getType().get(0).getCode(); + if ("id".equals(childSliceName)) { + typeCode = "http://hl7.org/fhirpath/System.String"; + } + if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { element.setDataType(GeneratorUtils.getInstance().resolveDataType(typeCode)); } else { diff --git a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java index 07a924e..6a4f1ba 100644 --- a/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java +++ b/native/fhir-to-bal-lib/src/main/java/org/wso2/healthcare/fhir/ballerina/packagegen/tool/modelgen/versions/r5/R5ExtensionContextGenerator.java @@ -88,6 +88,8 @@ public void populateBaseExtensionContext() { // Give a type for PrimitiveType Extensions marked with "_" // E.g.: type [{_code:{...}}] typeCode = "Extension"; + } else if ("id".equals(elementName)) { + typeCode = "http://hl7.org/fhirpath/System.String"; } if (GeneratorUtils.getInstance().shouldReplacedByBalType(typeCode)) { @@ -166,6 +168,11 @@ public void populateSliceExtensionContext() { /// Read the datatype of the element from the Type.Ref component /// TypeCode usually have only one element. String typeCode = elementDefinition.getType().get(0).getCode(); + + if ("id".equals(childSliceName)) { + typeCode = "http://hl7.org/fhirpath/System.String"; + } + if (typeCode == null) { // Special Case: TypeCode is null // Give a type for PrimitiveType Extensions marked with "_"