From 2c46cfe690528221bca4ad58372a9e736362ff45 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 24 Jun 2022 11:41:19 +0200 Subject: [PATCH] Add loading R5 extensions --- .../fhir/r5/conformance/ProfileUtilities.java | 106 +++++++++++++++++- .../fhir/r5/context/SimpleWorkerContext.java | 2 +- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index a1d4f6230..5c0f1d171 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -56,13 +56,17 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution; import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.elementmodel.ObjectConverter; import org.hl7.fhir.r5.elementmodel.Property; import org.hl7.fhir.r5.formats.IParser; +import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeType; import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.Coding; @@ -106,6 +110,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UsageContext; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.renderers.TerminologyRenderer; @@ -125,6 +130,10 @@ import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; +import org.hl7.fhir.utilities.npm.BasePackageCacheManager; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; @@ -6838,4 +6847,99 @@ public class ProfileUtilities extends TranslatingUtilities { this.masterSourceFileNames = masterSourceFileNames; } -} \ No newline at end of file + public static int loadR5Extensions(BasePackageCacheManager pcm, IWorkerContext context) throws FHIRException, IOException { + NpmPackage npm = pcm.loadPackage("hl7.fhir.r5.core", "current"); + String[] types = new String[] { "StructureDefinition", "ValueSet", "CodeSystem" }; + Map valueSets = new HashMap<>(); + Map codeSystems = new HashMap<>(); + List extensions = new ArrayList<>(); + JsonParser json = new JsonParser(); + for (PackageResourceInformation pri : npm.listIndexedResources(types)) { + CanonicalResource r = (CanonicalResource) json.parse(npm.load(pri)); + if (r instanceof CodeSystem) { + codeSystems.put(r.getUrl(), (CodeSystem) r); + } else if (r instanceof ValueSet) { + valueSets.put(r.getUrl(), (ValueSet) r); + } else if (r instanceof StructureDefinition) { + extensions.add((StructureDefinition) r); + } + } + PackageVersion pd = new PackageVersion(npm.name(), npm.version(), npm.dateAsDate()); + int c = 0; + List typeNames = context.getTypeNames(); + for (StructureDefinition sd : extensions) { + if (sd.getType().equals("Extension") && sd.getDerivation() == TypeDerivationRule.CONSTRAINT && + !context.hasResource(StructureDefinition.class, sd.getUrl())) { + if (survivesStrippingTypes(sd, context, typeNames)) { + c++; + sd.setUserData("path", Utilities.pathURL(npm.getWebLocation(), "extension-"+sd.getId()+".html")); + context.cacheResourceFromPackage(sd, pd); + registerTerminologies(sd, context, valueSets, codeSystems, pd); + } + } + } + return c; + } + + private static void registerTerminologies(StructureDefinition sd, IWorkerContext context, Map valueSets, Map codeSystems, PackageVersion pd) { + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.hasBinding() && ed.getBinding().hasValueSet()) { + String vs = ed.getBinding().getValueSet(); + if (!context.hasResource(StructureDefinition.class, vs)) { + loadValueSet(vs, context, valueSets, codeSystems, pd); + } + } + } + + } + + private static void loadValueSet(String url, IWorkerContext context, Map valueSets, Map codeSystems, PackageVersion pd) { + if (valueSets.containsKey(url)) { + ValueSet vs = valueSets.get(url); + context.cacheResourceFromPackage(vs, pd); + for (ConceptSetComponent inc : vs.getCompose().getInclude()) { + for (CanonicalType t : inc.getValueSet()) { + loadValueSet(t.asStringValue(), context, valueSets, codeSystems, pd); + } + if (inc.hasSystem()) { + if (!context.hasResource(CodeSystem.class, inc.getSystem()) && codeSystems.containsKey(inc.getSystem())) { + context.cacheResourceFromPackage(codeSystems.get(inc.getSystem()), pd); + } + } + } + } + + } + + private static boolean survivesStrippingTypes(StructureDefinition sd, IWorkerContext context, List typeNames) { + for (ElementDefinition ed : sd.getDifferential().getElement()) { + stripTypes(ed, context, typeNames); + } + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (!stripTypes(ed, context, typeNames)) { + return false; + } + } + return true; + } + + private static boolean stripTypes(ElementDefinition ed, IWorkerContext context, List typeNames) { + if (!ed.getPath().contains(".") || !ed.hasType()) { + return true; + } + ed.getType().removeIf(tr -> !typeNames.contains(tr.getWorkingCode())); + if (!ed.hasType()) { + return false; + } + for (TypeRefComponent tr : ed.getType()) { + if (tr.hasTargetProfile()) { + tr.getTargetProfile().removeIf(n -> !context.hasResource(StructureDefinition.class, n.asStringValue())); + if (!tr.hasTargetProfile()) { + return false; + } + } + } + return true; + } + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index 0ba485cec..e58817c14 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -487,7 +487,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon t++; } catch (Exception e) { throw new FHIRException(formatMessage(I18nConstants.ERROR_READING__FROM_PACKAGE__, s, pi.name(), pi.version(), e.getMessage()), e); - } + } } } else { if (types.length == 0) {