From d4d5b42aa189946f952af83fa6a0b8fa1825d27b Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 31 May 2023 06:32:55 +0300 Subject: [PATCH] add Resource Dependency walker + Resource Minifier functionality --- .../fhir/convertors/misc/PackageMinifier.java | 39 ++ .../ResourceDependencyPackageBuilder.java | 140 ++++++ .../r5/utils/ResourceDependencyWalker.java | 418 ++++++++++++++++++ .../hl7/fhir/r5/utils/ResourceMinifier.java | 308 +++++++++++++ .../hl7/fhir/utilities/npm/NpmPackage.java | 9 + 5 files changed, 914 insertions(+) create mode 100644 org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PackageMinifier.java create mode 100644 org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ResourceDependencyPackageBuilder.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceDependencyWalker.java create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceMinifier.java diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PackageMinifier.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PackageMinifier.java new file mode 100644 index 000000000..544db8f12 --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PackageMinifier.java @@ -0,0 +1,39 @@ +package org.hl7.fhir.convertors.misc; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.utils.ResourceMinifier; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation; + +public class PackageMinifier { + + public static void main(String[] args) throws IOException { + new PackageMinifier().process(args[0], args[1]); + } + + private void process(String source, String target) throws FileNotFoundException, IOException { + FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true); + NpmPackage src = pcm.loadPackage(source); + NpmPackage tgt = NpmPackage.empty(); + tgt.setNpm(src.getNpm().deepCopy()); + tgt.getNpm().set("name", tgt.getNpm().asString("name")+".min"); + + ResourceMinifier min = new ResourceMinifier(); + for (PackageResourceInformation pri : src.listIndexedResources()) { + if (min.isMinified(pri.getResourceType())) { + Resource res = new JsonParser().parse(src.load(pri)); + min.minify(res); + tgt.addFile("package", res.fhirType()+"-"+res.getIdPart()+".json", new JsonParser().composeBytes(res), null); + } + } + tgt.save(new FileOutputStream(target)); + } + +} diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ResourceDependencyPackageBuilder.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ResourceDependencyPackageBuilder.java new file mode 100644 index 000000000..bc2a9867f --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ResourceDependencyPackageBuilder.java @@ -0,0 +1,140 @@ +package org.hl7.fhir.convertors.misc; + +import java.io.IOException; +import java.io.FileOutputStream; +import java.util.Arrays; +import java.util.List; + +import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.convertors.loaders.loaderR5.NullLoaderKnowledgeProviderR5; +import org.hl7.fhir.convertors.loaders.loaderR5.R2ToR5Loader; +import org.hl7.fhir.convertors.loaders.loaderR5.R3ToR5Loader; +import org.hl7.fhir.convertors.loaders.loaderR5.R4ToR5Loader; +import org.hl7.fhir.convertors.loaders.loaderR5.R5ToR5Loader; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.context.IWorkerContext.IContextResourceLoader; +import org.hl7.fhir.r5.context.SimpleWorkerContext; +import org.hl7.fhir.r5.context.SimpleWorkerContext.SimpleWorkerContextBuilder; +import org.hl7.fhir.r5.model.CapabilityStatement; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.test.utils.TestPackageLoader; +import org.hl7.fhir.r5.utils.ResourceDependencyWalker; +import org.hl7.fhir.r5.utils.ResourceDependencyWalker.IResourceDependencyNotifier; +import org.hl7.fhir.r5.utils.ResourceMinifier; +import org.hl7.fhir.utilities.VersionUtilities; +import org.hl7.fhir.utilities.json.JsonException; +import org.hl7.fhir.utilities.json.parser.JsonParser; +import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; + +public class ResourceDependencyPackageBuilder { + + private static final List LOADED_TYPES = + Arrays.asList("StructureDefinition", "CodeSystem", "ValueSet", "CapabilityStatement", "ConceptMap", "NamingSystem", "OperationDefinition", "SearchParameter", "Questionnaire"); + + public static void main(String[] args) throws IOException { + + FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true); + System.out.println("Load Core"); + NpmPackage src = pcm.loadPackage(VersionUtilities.packageForVersion(args[0])); + SimpleWorkerContext ctxt = new SimpleWorkerContextBuilder().withAllowLoadingDuplicates(true).fromPackage(src); + loadFromPackage(args[0], ctxt, pcm, args[1]); + + NpmResourceDependencyCollector pckBuilder = new NpmResourceDependencyCollector(); + pckBuilder.npm = makeNpm(args[3], ctxt.getVersion()); + pckBuilder.minify = "true".equals(args[5]); + + new ResourceDependencyWalker(ctxt, pckBuilder).walk(ctxt.fetchResource(CapabilityStatement.class, args[2])); + + pckBuilder.npm.save(new FileOutputStream(args[4])); + } + + private static NpmPackage makeNpm(String vid, String version) throws JsonException, IOException { + NpmPackage res = NpmPackage.empty(); + String name = vid.substring(0, vid.indexOf('#')); + String ver = vid.substring(vid.indexOf("#")+1); + res.setNpm(JsonParser.parseObject("{\"name\": \""+name+"\", \"version\": \""+ver+"\",\"fhirVersions\": [\""+version+"\"],\"type\": \"fhir.ig\",\"tools-version\": 3}")); + return res; + } + + + public static class NpmResourceDependencyCollector implements IResourceDependencyNotifier { + + private NpmPackage npm; + private boolean minify; + + private byte[] toBytes(Resource resource) throws IOException { + if (VersionUtilities.isR5Ver(npm.fhirVersion())) { + return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(resource); + } + if (VersionUtilities.isR4Plus(npm.fhirVersion())) { + org.hl7.fhir.r4.model.Resource r4 = VersionConvertorFactory_40_50.convertResource(resource); + return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(r4); + } + if (VersionUtilities.isR3Ver(npm.fhirVersion())) { + org.hl7.fhir.dstu3.model.Resource r3 = VersionConvertorFactory_30_50.convertResource(resource); + return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(r3); + } + if (VersionUtilities.isR2Ver(npm.fhirVersion())) { + org.hl7.fhir.dstu2.model.Resource r2 = VersionConvertorFactory_10_50.convertResource(resource); + return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(r2); + } + throw new Error("Unsupported version"); + } + + @Override + public void seeResource(Resource resource, String summaryId) { + if (minify) { + ResourceMinifier min = new ResourceMinifier(); + if (min.isMinified(resource.fhirType())) { + resource = resource.copy(); + min.minify(resource); + } else { + return; + } + } + try { + npm.addFile("package", resource.fhirType()+"-"+resource.getIdPart()+".json", toBytes(resource), null); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void brokenLink(String link) { + System.err.println("Broken Link: " +link); + } + } + + private static void loadFromPackage(String version, SimpleWorkerContext ctxt, FilesystemPackageCacheManager pcm, String pid) throws FHIRException, IOException { + NpmPackage npm = pcm.loadPackage(pid); + for (String dep : npm.dependencies()) { + if (!VersionUtilities.isCorePackage(dep)) { + loadFromPackage(version, ctxt, pcm, dep); + } + } + System.out.println("Load "+pid); + ctxt.loadFromPackage(npm, getLoader(version)); + } + + private static IContextResourceLoader getLoader(String version) { + if (VersionUtilities.isR2Ver(version)) { + return new R2ToR5Loader(LOADED_TYPES, new NullLoaderKnowledgeProviderR5()); + } + if (VersionUtilities.isR3Ver(version)) { + return new R3ToR5Loader(LOADED_TYPES, new NullLoaderKnowledgeProviderR5()); + } + if (VersionUtilities.isR4Ver(version)) { + return new R4ToR5Loader(LOADED_TYPES, new NullLoaderKnowledgeProviderR5(), version); + } + if (VersionUtilities.isR5Ver(version)) { + return new R5ToR5Loader(LOADED_TYPES, new NullLoaderKnowledgeProviderR5()); + } + return null; + } + + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceDependencyWalker.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceDependencyWalker.java new file mode 100644 index 000000000..d63278395 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceDependencyWalker.java @@ -0,0 +1,418 @@ +package org.hl7.fhir.r5.utils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CapabilityStatement; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; +import org.hl7.fhir.r5.model.ConceptMap; +import org.hl7.fhir.r5.model.ConceptMap.AdditionalAttributeComponent; +import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; +import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; +import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; +import org.hl7.fhir.r5.model.NamingSystem; +import org.hl7.fhir.r5.model.OperationDefinition; +import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent; +import org.hl7.fhir.r5.model.Questionnaire; +import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemAnswerOptionComponent; +import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemEnableWhenComponent; +import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemInitialComponent; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.SearchParameter; +import org.hl7.fhir.r5.model.SearchParameter.SearchParameterComponentComponent; +import org.hl7.fhir.r5.model.StructureDefinition; +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.model.ValueSet.ValueSetExpansionParameterComponent; +import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent; +import org.hl7.fhir.utilities.Utilities; + +public class ResourceDependencyWalker { + + public interface IResourceDependencyNotifier { + public void seeResource(Resource resource, String summaryId); + public void brokenLink(String link); + } + + public class NullResourceDependencyNotifier implements IResourceDependencyNotifier { + + @Override + public void seeResource(Resource resource, String summaryId) { + System.out.println(summaryId); + } + + @Override + public void brokenLink(String link) { + System.err.println("Broken Link: " +link); + } + } + + private IResourceDependencyNotifier notifier = new NullResourceDependencyNotifier(); + private IWorkerContext context; + private Set processedLinks = new HashSet<>(); + private Set processedResources = new HashSet<>(); + + public ResourceDependencyWalker(IWorkerContext context, IResourceDependencyNotifier notifier) { + super(); + this.notifier = notifier; + this.context = context; + } + + public ResourceDependencyWalker(IWorkerContext context) { + super(); + this.context = context; + } + + private void notify(Resource resource, String prefix) { + String summary = null; + if (resource instanceof CanonicalResource) { + summary = ((CanonicalResource) resource).getVersionedUrl(); + } else { + summary = resource.fhirType()+"/"+resource.getIdPart(); + } + if (resource.getSourcePackage() != null) { + summary = summary + " from "+resource.getSourcePackage(); + } + notifier.seeResource(resource, prefix+summary); + } + + public void walk(Resource res) { + notify(res, "Find Dependencies for "); + processedResources.add(res); + doWalk(res); + } + + private void walkIntoLink(String value, CanonicalResource source) { + if (value != null ) { + String key = source.getSourcePackage() == null ? value : value+" from "+source.getSourcePackage().getVID(); + if (!processedLinks.contains(key)) { + processedLinks.add(key); + Resource tgt = context.fetchResource(Resource.class, value, source); + if (tgt == null && Utilities.charCount(value, '/') == 1) { + tgt = context.fetchResourceById(value.substring(0, value.indexOf('/')), value.substring(value.indexOf('/')+1)); + } + if (tgt == null) { + notifier.brokenLink(key); + } else { + if (!processedResources.contains(tgt) && !isCore(tgt)) { + processedResources.add(tgt); + notify(tgt, "Depends On "); + doWalk(tgt); + } + } + } + } + } + + private boolean isCore(Resource tgt) { + return tgt.hasSourcePackage() && "hl7.fhir.r5.core".equals(tgt.getSourcePackage().getId()); + } + + private void doWalk(Resource res) { + if (res instanceof StructureDefinition) { + walkSD((StructureDefinition) res); + } else if (res instanceof ValueSet) { + walkVS((ValueSet) res); + } else if (res instanceof CodeSystem) { + walkCS((CodeSystem) res); + } else if (res instanceof CapabilityStatement) { + walkCS((CapabilityStatement) res); + } else if (res instanceof ConceptMap) { + walkCM((ConceptMap) res); + } else if (res instanceof NamingSystem) { + walkNS((NamingSystem) res); + } else if (res instanceof OperationDefinition) { + walkOD((OperationDefinition) res); + } else if (res instanceof SearchParameter) { + walkSP((SearchParameter) res); + } else if (res instanceof Questionnaire) { + walkQ((Questionnaire) res); + } else { + throw new Error("Resource "+res.fhirType()+" not Processed yet"); + } + } + + + private void walkSP(SearchParameter sp) { + walkCR(sp); + walkIntoLink(sp.getDerivedFrom(), sp); + for (SearchParameterComponentComponent spc : sp.getComponent()) { + walkIntoLink(spc.getDefinition(), sp); + } + } + + private void walkQ(Questionnaire q) { + walkCR(q); + walkCT(q.getDerivedFrom(), q); + for (QuestionnaireItemComponent item : q.getItem()) { + walkQItem(item, q); + } + } + + private void walkQItem(QuestionnaireItemComponent item, Questionnaire q) { + walkIntoLink(item.getDefinition(), q); + walkIntoLink(item.getAnswerValueSet(), q); + for (QuestionnaireItemEnableWhenComponent ew : item.getEnableWhen()) { + if (ew.hasAnswerReference()) { + walkIntoLink(ew.getAnswerReference().getReference(), q); + } + } + for (QuestionnaireItemAnswerOptionComponent ao : item.getAnswerOption()) { + if (ao.hasValueReference()) { + walkIntoLink(ao.getValueReference().getReference(), q); + } + } + for (QuestionnaireItemInitialComponent iv : item.getInitial()) { + if (iv.hasValueReference()) { + walkIntoLink(iv.getValueReference().getReference(), q); + } + } + walkIntoLink(item.getDefinition(), q); + for (QuestionnaireItemComponent child : item.getItem()) { + walkQItem(child, q); + } + } + + private void walkOD(OperationDefinition od) { + walkCR(od); + walkIntoLink(od.getBase(), od); + walkIntoLink(od.getInputProfile(), od); + walkIntoLink(od.getOutputProfile(), od); + + for (OperationDefinitionParameterComponent p : od.getParameter()) { + walkODP(od, p); + } + } + + private void walkODP(OperationDefinition od, OperationDefinitionParameterComponent p) { + walkCT(p.getTargetProfile(), od); + walkIntoLink(p.getBinding().getValueSet(), od); + for (OperationDefinitionParameterComponent pp : p.getPart()) { + walkODP(od, pp); + } + } + + private void walkNS(NamingSystem ns) { + walkCR(ns); + } + + private void walkCM(ConceptMap cm) { + walkCR(cm); + walkRA(cm.getRelatedArtifact(), cm); + + for (org.hl7.fhir.r5.model.ConceptMap.PropertyComponent prop : cm.getProperty()) { + walkIntoLink(prop.getUri(), cm); + } + for (AdditionalAttributeComponent attr : cm.getAdditionalAttribute()) { + walkIntoLink(attr.getUri(), cm); + } + walkIntoLink(cm.getSourceScope().primitiveValue(), cm); + walkIntoLink(cm.getTargetScope().primitiveValue(), cm); + + for (ConceptMapGroupComponent group : cm.getGroup()) { + walkIntoLink(group.getSource(), cm); + walkIntoLink(group.getTarget(), cm); + walkIntoLink(group.getUnmapped().getValueSet(), cm); + walkIntoLink(group.getUnmapped().getOtherMap(), cm); + for (SourceElementComponent elem : group.getElement()) { + walkIntoLink(elem.getValueSet(), cm); + for (TargetElementComponent tgt : elem.getTarget()) { + walkIntoLink(tgt.getValueSet(), cm); + } + } + } + } + + private void walkCS(CapabilityStatement cs) { + walkCR(cs); + walkCT(cs.getInstantiates(), cs); + walkCT(cs.getImports(), cs); + walkCT(cs.getImplementationGuide(), cs); + + for (CapabilityStatementRestComponent rest : cs.getRest()) { + + for (CapabilityStatementRestResourceComponent res : rest.getResource()) { + walkIntoLink(res.getProfile(), cs); + walkCT(res.getSupportedProfile(), cs); + for (CapabilityStatementRestResourceSearchParamComponent srch : res.getSearchParam()) { + walkIntoLink(srch.getDefinition(), cs); + } + for (CapabilityStatementRestResourceOperationComponent op : res.getOperation()) { + walkIntoLink(op.getDefinition(), cs); + } + } + for (CapabilityStatementRestResourceSearchParamComponent srch : rest.getSearchParam()) { + walkIntoLink(srch.getDefinition(), cs); + } + for (CapabilityStatementRestResourceOperationComponent op : rest.getOperation()) { + walkIntoLink(op.getDefinition(), cs); + } + } + } + + private void walkCS(CodeSystem cs) { + walkCR(cs); + walkRA(cs.getRelatedArtifact(), cs); + if (cs.hasValueSet()) { + walkIntoLink(cs.getValueSet(), cs); + } + if (cs.hasSupplements()) { + walkIntoLink(cs.getSupplements(), cs); + } + + for (PropertyComponent p : cs.getProperty()) { + if (p.hasUri()) { + walkIntoLink(p.getUri(), cs); + } + } + } + + private void walkVS(ValueSet vs) { + walkCR(vs); + walkRA(vs.getRelatedArtifact(), vs); + for (ConceptSetComponent inc : vs.getCompose().getInclude()) { + walkVSInc(inc, vs); + } + for (ConceptSetComponent inc : vs.getCompose().getExclude()) { + walkVSInc(inc, vs); + } + if (vs.hasExpansion()) { + ValueSetExpansionComponent exp = vs.getExpansion(); + for (ValueSetExpansionParameterComponent p : exp.getParameter()) { + if (p.hasValueUriType()) { + walkIntoLink(p.getValueUriType().primitiveValue(), vs); + } + } + for (ValueSetExpansionPropertyComponent p : exp.getProperty()) { + if (p.hasUri()) { + walkIntoLink(p.getUri(), vs); + } + } + for (ValueSetExpansionContainsComponent cc : exp.getContains()) { + walkCC(cc, vs); + } + } + } + + private void walkCC(ValueSetExpansionContainsComponent cc, ValueSet vs) { + walkIntoLink(cc.getSystem(), vs); + for (ValueSetExpansionContainsComponent ccc : cc.getContains()) { + walkCC(ccc, vs); + } + } + + private void walkVSInc(ConceptSetComponent inc, ValueSet vs) { + walkCT(inc.getValueSet(), vs); + walkIntoLink(inc.getSystem(), vs); + } + + private void walkCT(List list, CanonicalResource source) { + for (CanonicalType ct : list) { + walkIntoLink(ct.getValue(), source); + } + } + + private void walkRA(List list, CanonicalResource source) { + for (RelatedArtifact ra : list) { + walkRA(ra, source); + } + } + + private void walkRA(RelatedArtifact ra, CanonicalResource source) { + if (ra.hasResource()) { + walkIntoLink(ra.getResource(), source); + } + if (ra.hasResourceReference()) { + walkIntoLink(ra.getResourceReference().getReference(), source); + } + } + + private void walkSD(StructureDefinition sd) { + walkCR(sd); + walkIntoLink(sd.getBaseDefinition(), sd); + for (ElementDefinition ed : sd.getDifferential().getElement()) { + walkED(ed, sd); + } + } + + private void walkED(ElementDefinition ed, StructureDefinition sd) { + for (TypeRefComponent type : ed.getType()) { + if (Utilities.isAbsoluteUrl(type.getCode())) { + walkIntoLink(type.getCode(), sd); + } + walkCT(type.getProfile(), sd); + walkCT(type.getTargetProfile(), sd); + } + walkCT(ed.getValueAlternatives(), sd); + if (ed.hasBinding()) { + ElementDefinitionBindingComponent b = ed.getBinding(); + if (b.hasValueSet()) { + walkIntoLink(b.getValueSet(), sd); + } + for (ElementDefinitionBindingAdditionalComponent ab : b.getAdditional()) { + if (ab.hasValueSet()) { + walkIntoLink(ab.getValueSet(), sd); + } + if (ab.hasUsage()) { + walkUsage(ab.getUsage(), sd); + } + } + } + } + + private void walkUsage(List usageList, CanonicalResource source) { + for (UsageContext usage : usageList) { + walkUsage(usage, source); + } + } + + private void walkUsage(UsageContext usage, CanonicalResource source) { + if (usage.hasValueReference()) { + walkReference(usage.getValueReference(), source); + } + } + + private void walkReference(Reference ref, CanonicalResource source) { + if (ref.hasReference()) { + walkIntoLink(ref.getReference(), source); + } + } + + private void walkCR(CanonicalResource cr) { + walkDR(cr); + walkUsage(cr.getUseContext(), cr); + } + + private void walkDR(DomainResource dr) { + walkRes(dr); + for (Resource res : dr.getContained()) { + walk(res); + } + } + + private void walkRes(Resource res) { + // nothing + } + + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceMinifier.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceMinifier.java new file mode 100644 index 000000000..397078c22 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceMinifier.java @@ -0,0 +1,308 @@ +package org.hl7.fhir.r5.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.CapabilityStatement; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent; +import org.hl7.fhir.r5.model.CapabilityStatement.SystemInteractionComponent; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent; +import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; +import org.hl7.fhir.r5.model.ConceptMap; +import org.hl7.fhir.r5.model.ConceptMap.AdditionalAttributeComponent; +import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; +import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; +import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; +import org.hl7.fhir.r5.model.NamingSystem; +import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent; +import org.hl7.fhir.r5.model.OperationDefinition; +import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionOverloadComponent; +import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent; +import org.hl7.fhir.r5.model.Questionnaire; +import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.utilities.Utilities; + +public class ResourceMinifier { + + private static final List VALIDATION_EXTENSIONS = + Arrays.asList(ToolingExtensions.EXT_OBLIGATION_INHERITS, ToolingExtensions.EXT_OBLIGATION_PROFILE_FLAG); + + public boolean isMinified(String resName) { + return Utilities.existsInList(resName, "StructureDefinition", "CodeSystem", "ValueSet", "CapabilityStatement", + "ConceptMap", "NamingSystem", "OperationDefinition", "SearchParameter", "Questionnaire"); + } + + public void minify(Resource res) { + if (res instanceof StructureDefinition) { + minifySD((StructureDefinition) res); + } + if (res instanceof ValueSet) { + minifyVS((ValueSet) res); + } + if (res instanceof CodeSystem) { + minifyCS((CodeSystem) res); + } + if (res instanceof CapabilityStatement) { + minifyCS((CapabilityStatement) res); + } + if (res instanceof ConceptMap) { + minifyCM((ConceptMap) res); + } + if (res instanceof NamingSystem) { + minifyNS((NamingSystem) res); + } + + if (res instanceof OperationDefinition) { + minifyOD((OperationDefinition) res); + } + + if (res instanceof Questionnaire) { + minifyQ((Questionnaire) res); + } + } + + private void minifyQ(Questionnaire q) { + minCR(q); + + q.setApprovalDate(null); + q.setLastReviewDate(null); + q.setEffectivePeriod(null); + q.setCode(null); + for (QuestionnaireItemComponent item : q.getItem()) { + minifyQItem(item); + + } + } + + private void minifyQItem(QuestionnaireItemComponent item) { + item.setCode(null); + item.setPrefix(null); + item.setText(null); + for (QuestionnaireItemComponent child : item.getItem()) { + minifyQItem(child); + } + } + + private void minifyOD(OperationDefinition od) { + minCR(od); + od.setComment(null); + for (OperationDefinitionParameterComponent p : od.getParameter()) { + minifyODP(p); + } + for (OperationDefinitionOverloadComponent ol : od.getOverload()) { + ol.setComment(null); + } + } + + private void minifyODP(OperationDefinitionParameterComponent p) { + p.setDocumentation(null); + for (OperationDefinitionParameterComponent pp : p.getPart()) { + minifyODP(pp); + } + } + + private void minifyNS(NamingSystem ns) { + minCR(ns); + + ns.setApprovalDate(null); + ns.setLastReviewDate(null); + ns.setEffectivePeriod(null); + ns.setTopic(null); + ns.setAuthor(null); + ns.setEditor(null); + ns.setReviewer(null); + ns.setEndorser(null); + ns.setRelatedArtifact(null); + + ns.setUsage(null); + for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) { + id.setComment(null); + } + } + + private void minifyCM(ConceptMap cm) { + minCR(cm); + + cm.setApprovalDate(null); + cm.setLastReviewDate(null); + cm.setEffectivePeriod(null); + cm.setTopic(null); + cm.setAuthor(null); + cm.setEditor(null); + cm.setReviewer(null); + cm.setEndorser(null); + cm.setRelatedArtifact(null); + + for (org.hl7.fhir.r5.model.ConceptMap.PropertyComponent prop : cm.getProperty()) { + prop.setDescription(null); + } + for (AdditionalAttributeComponent attr : cm.getAdditionalAttribute()) { + attr.setDescription(null); + } + for (ConceptMapGroupComponent group : cm.getGroup()) { + for (SourceElementComponent elem : group.getElement()) { + for (TargetElementComponent tgt : elem.getTarget()) { + tgt.setComment(null); + } + } + } + } + + private void minifyCS(CapabilityStatement cs) { + minCR(cs); + + cs.setSoftware(null); + cs.setImplementation(null); + for (CapabilityStatementRestComponent rest : cs.getRest()) { + rest.setDocumentation(null); + rest.setSecurity(null); + for (CapabilityStatementRestResourceComponent res : rest.getResource()) { + res.setDocumentation(null); + for (ResourceInteractionComponent intr : res.getInteraction()) { + intr.setDocumentation(null); + } + for (CapabilityStatementRestResourceSearchParamComponent srch : res.getSearchParam()) { + srch.setDocumentation(null); + } + for (CapabilityStatementRestResourceOperationComponent op : res.getOperation()) { + op.setDocumentation(null); + } + } + for (SystemInteractionComponent intr : rest.getInteraction()) { + intr.setDocumentation(null); + } + for (CapabilityStatementRestResourceSearchParamComponent srch : rest.getSearchParam()) { + srch.setDocumentation(null); + } + for (CapabilityStatementRestResourceOperationComponent op : rest.getOperation()) { + op.setDocumentation(null); + } + } + cs.setMessaging(null); + cs.setDocument(null); + } + + private void minifyCS(CodeSystem cs) { + minCR(cs); + + cs.setApprovalDate(null); + cs.setLastReviewDate(null); + cs.setEffectivePeriod(null); + cs.setTopic(null); + cs.setAuthor(null); + cs.setEditor(null); + cs.setReviewer(null); + cs.setEndorser(null); + cs.setRelatedArtifact(null); + + for (CodeSystemFilterComponent filter : cs.getFilter()) { + filter.setDescription(null); + } + for (PropertyComponent prop : cs.getProperty()) { + prop.setDescription(null); + } + for (ConceptDefinitionComponent cc : cs.getConcept()) { + minify(cc); + } + } + + private void minify(ConceptDefinitionComponent cc) { + cc.setDefinition(null); + for (ConceptDefinitionComponent ccc : cc.getConcept()) { + minify(ccc); + } + } + + private void minifyVS(ValueSet vs) { + minCR(vs); + + vs.setApprovalDate(null); + vs.setLastReviewDate(null); + vs.setEffectivePeriod(null); + vs.setTopic(null); + vs.setAuthor(null); + vs.setEditor(null); + vs.setReviewer(null); + vs.setEndorser(null); + vs.setRelatedArtifact(null); + // can't remove anything else + } + + private void minifySD(StructureDefinition sd) { + minCR(sd); + sd.setKeyword(null); + sd.setMapping(null); + sd.setSnapshot(null); + for (ElementDefinition ed : sd.getDifferential().getElement()) { + minifyED(ed); + } + } + + private void minifyED(ElementDefinition ed) { + ed.setLabel(null); + ed.setCode(null); + ed.getSlicing().setDescription(null); + ed.setShort(null); + ed.setDefinition(null); + ed.setComment(null); + ed.setRequirements(null); + ed.setAlias(null); + ed.setDefaultValue(null); + ed.setOrderMeaning(null); + ed.setMeaningWhenMissing(null); + ed.setExample(null); + ed.setIsModifierReason(null); + ed.getBinding().setDescription(null); + for (ElementDefinitionBindingAdditionalComponent abn : ed.getBinding().getAdditional()) { + abn.setDocumentation(null); + abn.setShortDoco(null); + } + ed.setMapping(null); + } + + private void minCR(CanonicalResource cr) { + minDR(cr); + cr.setIdentifier(null); + cr.setPublisher(null); + cr.setContact(null); + cr.setDescription(null); + cr.setPurpose(null); + cr.setCopyright(null); + cr.setCopyrightLabel(null); + } + + private void minDR(DomainResource dr) { + minRes(dr); + dr.setText(null); + List del = new ArrayList<>(); + for (Resource res : dr.getContained()) { + if (isMinified(res.fhirType())) { + minify(res); + } else { + del.add(res); + } + } + dr.getContained().removeAll(del); + dr.getExtension().removeIf(ext -> !Utilities.existsInList(ext.getUrl(), VALIDATION_EXTENSIONS)); + } + + private void minRes(Resource res) { + res.setMeta(null); + res.setImplicitRules(null); + } + +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java index 70a46f406..e9dc0283e 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java @@ -719,6 +719,15 @@ public class NpmPackage { return npm; } + /** + * should only use this when npm == null + * + * @param npm + */ + public void setNpm(JsonObject npm) { + this.npm = npm; + } + /** * convenience method for getting the package name * @return