From f8f7ae7e0faa877e81d5acad5c34fb37326377d9 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 18 Nov 2024 07:06:50 +1100 Subject: [PATCH 1/3] Change to use UserDataNames --- .../publisher/CodeSystemValidator.java | 5 +-- .../publisher/IGKnowledgeProvider.java | 11 +++--- .../publisher/PublisherTranslator.java | 3 +- .../igtools/publisher/ValidationServices.java | 3 +- .../publisher/loaders/PublisherLoader.java | 5 +-- .../publisher/modules/CrossVersionModule.java | 13 +++---- .../modules/xver/XVerAnalysisEngine.java | 35 ++++++++++--------- .../fhir/igtools/renderers/BaseRenderer.java | 3 +- .../igtools/renderers/CrossViewRenderer.java | 25 ++++++------- .../igtools/renderers/DependencyRenderer.java | 3 +- .../renderers/StructureMapRenderer.java | 5 +-- .../igtools/renderers/ValueSetRenderer.java | 3 +- .../spreadsheets/CodeSystemConvertor.java | 15 ++++---- .../spreadsheets/IgSpreadsheetParser.java | 27 +++++++------- ...bservationSummarySpreadsheetGenerator.java | 3 +- 15 files changed, 87 insertions(+), 72 deletions(-) diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/CodeSystemValidator.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/CodeSystemValidator.java index d3552a9fd..0d1c690f1 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/CodeSystemValidator.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/CodeSystemValidator.java @@ -40,6 +40,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.r5.utils.validation.ValidatorSession; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.validation.BaseValidator; @@ -52,8 +53,8 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS */ public class CodeSystemValidator extends BaseValidator { - public CodeSystemValidator(IWorkerContext context, XVerExtensionManager xverManager) { - super(context, xverManager, false); + public CodeSystemValidator(IWorkerContext context, XVerExtensionManager xverManager, ValidatorSession session) { + super(context, xverManager, false, session); } public List validate(CodeSystem cs, boolean forBuild) { diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/IGKnowledgeProvider.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/IGKnowledgeProvider.java index 8b8bc0e06..a4c1b1fd6 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/IGKnowledgeProvider.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/IGKnowledgeProvider.java @@ -47,6 +47,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.LoincLinker; import org.hl7.fhir.utilities.Utilities; @@ -426,7 +427,7 @@ else if ((bc.getUrl().startsWith(canonical) || (altCanonical != null && bc.getUr if (r.getConfig() == null) findConfiguration(f, r); JsonObject e = r.getConfig(); - bc.setUserData("config", e); + bc.setUserData(UserDataNames.pub_resource_config, e); String base = getProperty(r, "base"); if (base != null) bc.setWebPath(doReplacements(base, r, null, null)); @@ -449,7 +450,7 @@ public void checkForPath(FetchedFile f, FetchedResource r, Element res) throws F if (r.getConfig() == null) findConfiguration(f, r); JsonObject e = r.getConfig(); - res.setUserData("config", e); + res.setUserData(UserDataNames.pub_resource_config, e); String base = getProperty(r, "base"); if (base != null) res.setWebPath(doReplacements(base, r, null, null)); @@ -586,7 +587,7 @@ else if (ref.equals("http://www.iso.org/iso/country_codes.htm")) br.url = vs.getWebPath(); br.display = vs.getName(); br.uri = vs.getVersionedUrl(); - br.external = vs.hasUserData("External.Link"); + br.external = vs.hasUserData(UserDataNames.render_external_link); } } else { if (ref.startsWith("http://hl7.org/fhir/ValueSet/")) { @@ -595,7 +596,7 @@ else if (ref.equals("http://www.iso.org/iso/country_codes.htm")) br.url = vs.getWebPath(); br.display = vs.getName(); br.uri = vs.getUrl(); - br.external = vs.hasUserData("External.Link"); + br.external = vs.hasUserData(UserDataNames.render_external_link); } else { String nvref = ref; if (nvref.contains("|")) { @@ -641,7 +642,7 @@ else if (ref.equals("http://www.iso.org/iso/country_codes.htm")) br.display = vs.getName(); } br.uri = vs.getUrl(); - br.external = vs.hasUserData("External.Link"); + br.external = vs.hasUserData(UserDataNames.render_external_link); } else if (ref.startsWith("http://cts.nlm.nih.gov/fhir/ValueSet/")) { String oid = ref.substring("http://cts.nlm.nih.gov/fhir/ValueSet/".length()); br.url = "https://vsac.nlm.nih.gov/valueset/"+oid+"/expansion"; diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/PublisherTranslator.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/PublisherTranslator.java index 0ec596335..b282922c6 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/PublisherTranslator.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/PublisherTranslator.java @@ -7,6 +7,7 @@ import org.hl7.fhir.r5.context.SimpleWorkerContext; import org.hl7.fhir.r5.elementmodel.LanguageUtils; import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.JsonLangFileProducer; import org.hl7.fhir.utilities.i18n.LanguageFileProducer.TranslationUnit; @@ -84,7 +85,7 @@ private void genTranslations(FetchedFile f, FetchedResource r, String lang, bool if (res != null && lu.handlesAsResource(res)) { List translations = lu.generateTranslations(res, lang); - String srcFile = res.hasUserData("source.filename") ? res.getUserString("source.filename") : r.fhirType()+"-"+r.getId(); + String srcFile = res.hasUserData(UserDataNames.pub_source_filename) ? res.getUserString(UserDataNames.pub_source_filename) : r.fhirType()+"-"+r.getId(); po.produce(srcFile, baseLang, lang, translations, srcFile+".po"); xliff.produce(srcFile, baseLang, lang, translations, srcFile+".xliff"); diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/ValidationServices.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/ValidationServices.java index de40ededb..24f46297c 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/ValidationServices.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/ValidationServices.java @@ -59,6 +59,7 @@ import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.ImplicitValueSets; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.r5.utils.validation.IMessagingServices; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; @@ -109,7 +110,7 @@ public Element fetch(IResourceValidator validator, Object appContext, String url String turl = (!Utilities.isAbsoluteUrl(url)) ? Utilities.pathURL(ipg.getCanonical(), url) : url; Resource res = context.fetchResource(getResourceType(turl), turl); if (res != null) { - Element e = (Element)res.getUserData("element"); + Element e = (Element)res.getUserData(UserDataNames.pub_element); if (e!=null) return e; else diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/loaders/PublisherLoader.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/loaders/PublisherLoader.java index 60656b9bf..568102aa7 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/loaders/PublisherLoader.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/loaders/PublisherLoader.java @@ -17,6 +17,7 @@ import org.hl7.fhir.r5.context.IContextResourceLoader; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.npm.NpmPackage; @@ -107,9 +108,9 @@ private String getIgPath(Resource r) { } r.setWebPath(path); if (path.contains("vsac")) { - r.setUserData("External.Link", "https://vsac.nlm.nih.gov"); + r.setUserData(UserDataNames.render_external_link, "https://vsac.nlm.nih.gov"); } - r.setUserData("webroot", pathToSpec); + r.setUserData(UserDataNames.render_webroot, pathToSpec); // String v = ((CanonicalResource) r).getVersion(); return path; } diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/CrossVersionModule.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/CrossVersionModule.java index 380d5fa71..7e2526899 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/CrossVersionModule.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/CrossVersionModule.java @@ -61,6 +61,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.utils.ResourceSorters; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; @@ -390,7 +391,7 @@ private void genVersionType(String path, StructureDefinition sd, ImplementationG for (int i = 0; i < rowCount; i++) { rows.add(tbl.tr()); } - ed.setUserData("rows", rows); + ed.setUserData(UserDataNames.xver_rows, rows); renderElementRow(sd, columns, ed, rowCount, rows); } @@ -399,7 +400,7 @@ private void genVersionType(String path, StructureDefinition sd, ImplementationG for (SourcedElementDefinition m : missingChains) { StructureDefinitionColumn rootCol = engine.getColumn(columns, m.getSd()); ElementDefinition prevED = findPreviousElement(m, rootCol.getElements()); // find the anchor that knows which rows we'll be inserting under - List rows = (List) prevED.getUserData("rows"); + List rows = (List) prevED.getUserData(UserDataNames.xver_rows); XhtmlNode lastTR = rows.get(rows.size()-1); for (StructureDefinitionColumn col : columns) { col.clear(); @@ -423,7 +424,7 @@ private void genVersionType(String path, StructureDefinition sd, ImplementationG for (int i = 0; i < rowCount; i++) { rows.add(tbl.tr(lastTR)); } - m.getEd().setUserData("rows", rows); + m.getEd().setUserData(UserDataNames.xver_rows, rows); renderElementRow(m.getSd(), columns, m.getEd(), rowCount, rows); } @@ -438,7 +439,7 @@ private void genVersionType(String path, StructureDefinition sd, ImplementationG name = VersionUtilities.getNameForVersion(srcscope)+" "+srcscope.substring(srcscope.lastIndexOf("#")+1); } String tgtscope = link.getNextCM().getTargetScope().primitiveValue(); - link.getNextCM().setUserData("presentation", + link.getNextCM().setUserData(UserDataNames.render_presentation, VersionUtilities.getNameForVersion(tgtscope)+" "+tgtscope.substring(tgtscope.lastIndexOf("#")+1)); maps.add(link.getNextCM()); } @@ -510,7 +511,7 @@ private void findLinkedStructures(Set res, SourcedElementDe private void checkForCodeTranslations(List> codeChains, ElementDefinitionLink link) { - if (link.getPrev().getEd().hasUserData("links."+MakeLinkMode.ORIGIN_CHAIN)) { + if (link.getPrev().getEd().hasUserData(UserDataNames.xver_links+"."+MakeLinkMode.ORIGIN_CHAIN)) { List originChain = engine.makeEDLinks(link.getPrev().getEd(), MakeLinkMode.ORIGIN_CHAIN); boolean mappings = false; for (ElementDefinitionLink olink : originChain) { @@ -635,7 +636,7 @@ private void genSummaryPages(String path) throws IOException { private XhtmlNode rendererElementForType(XhtmlNode td, StructureDefinition sdt, ElementDefinition ed, IWorkerContext context, String origName) { if (ed.getPath().contains(".")) { - SourcedElementDefinition sed = (SourcedElementDefinition) ed.getUserData("sed"); + SourcedElementDefinition sed = (SourcedElementDefinition) ed.getUserData(UserDataNames.xver_sed); if (sed != null) { if (sed.getExtension() == null) { td.imgT("icon-extension-no.png", "No cross-version extension allowed for this element because "+sed.getStatusReason()); diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/xver/XVerAnalysisEngine.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/xver/XVerAnalysisEngine.java index 4d66efee7..76797dc66 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/xver/XVerAnalysisEngine.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/modules/xver/XVerAnalysisEngine.java @@ -79,6 +79,7 @@ import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.TranslatedCode; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; @@ -250,7 +251,7 @@ private boolean execute(String folder) throws FHIRException, IOException { checkStructureMaps(); for (ConceptMap cm : conceptMaps.values()) { - if (cm.hasUserData("cm.used") && "false".equals(cm.getUserString("cm.used"))) { + if (cm.hasUserData(UserDataNames.xver_cm_used) && "false".equals(cm.getUserString(UserDataNames.xver_cm_used))) { if (!cm.getId().contains("4to5") && !cm.getId().contains("5to4")) { qaMsg("Unused conceptmap: "+cm.getId(), false); } @@ -282,7 +283,7 @@ private void generateBackboneElements(StructureDefinition sd) { for (String s : Utilities.sorted(urls.keySet())) { ElementDefinition ed = new ElementDefinition("Extension.extension"); sd.getDifferential().getElement().add(i, ed); - ed.setSliceName(urls.get(s).getUserString("sliceName")); + ed.setSliceName(urls.get(s).getUserString(UserDataNames.xver_sliceName)); ed.addType().setCode("Extension").addProfile(s); i++; } @@ -324,7 +325,7 @@ private SourcedElementDefinition getParentElement(SourcedElementDefinition eleme } path = path.substring(0, path.lastIndexOf(".")); ElementDefinition ed = element.getSd().getDifferential().getElementByPath(path); - return (SourcedElementDefinition) ed.getUserData("sed"); + return (SourcedElementDefinition) ed.getUserData(UserDataNames.xver_sed); } private void populateTypeMap() { @@ -378,7 +379,7 @@ private void checkCreateExtension(ElementDefinitionLink link, SourcedElementDefi StructureDefinition sd = new StructureDefinition(); element.setExtension(sd); extensions.add(sd); - sd.setUserData("sliceName", element.getEd().getName()+VersionUtilities.getNameForVersion(element.getVer())); + sd.setUserData(UserDataNames.xver_sliceName, element.getEd().getName()+VersionUtilities.getNameForVersion(element.getVer())); sd.setUrl(element.extensionPath()); String id = generateConciseId("xv-"+VersionUtilities.getNameForVersion(element.getVer()).toLowerCase()+"-"+element.getEd().getPath()); if (extMap.containsKey(id)) { @@ -410,7 +411,7 @@ private void checkCreateExtension(ElementDefinitionLink link, SourcedElementDefi ElementDefinition ed = element.getSd().getDifferential().getElementByPath(thisVersionParent); for (String tgtVer : allVersions()) { if (element.appliesToVersion(tgtVer)) { - List contexts = getParentContextsForVersion((SourcedElementDefinition) ed.getUserData("sed"), ver, tgtVer); + List contexts = getParentContextsForVersion((SourcedElementDefinition) ed.getUserData(UserDataNames.xver_sed), ver, tgtVer); for (StructureDefinitionContextComponent ctxt : contexts) { sd.addContext(ctxt); } @@ -1217,7 +1218,7 @@ private void checkStructureMap(StructureMap map) throws FileNotFoundException, I if (cm == null) { qaMsg("bed ref '"+url+"' in "+map.getUrl(), true); } else { - cm.setUserData("cm.used", "true"); + cm.setUserData(UserDataNames.xver_cm_used, "true"); } } } @@ -1411,7 +1412,7 @@ private Map processRuleTarget(StructureMap map, String qaMsg("bad ref '"+url+"' dstVer is "+cmDstVer+" should be "+dstVer + " in "+map.getUrl(), true); } else { checkCM(cm, source.toSED(), target.toSED(), vsl, vsr); - cm.setUserData("cm.used", "true"); + cm.setUserData(UserDataNames.xver_cm_used, "true"); } } } @@ -1710,7 +1711,7 @@ private void loadConceptMaps(File file, boolean track) throws FHIRFormatError, F throw new Error("Error parsing "+f.getAbsolutePath()+": "+e.getMessage(), e); } if (track) { - cm.setUserData("cm.used", "false"); + cm.setUserData(UserDataNames.xver_cm_used, "false"); } cm.setWebPath("ConceptMap-"+cm.getId()+".html"); conceptMaps.put(id.toLowerCase(), cm); @@ -2279,7 +2280,7 @@ private String toString(Set codes) { } private void checkCM(ConceptMap cm, SourcedElementDefinition se, SourcedElementDefinition de, VSPair s, VSPair d) throws FileNotFoundException, IOException { - cm.setUserData("cm.used", "true"); + cm.setUserData(UserDataNames.xver_cm_used, "true"); boolean mod = false; String scopeUri = "http://hl7.org/fhir/"+VersionUtilities.getMajMin(se.getVer())+"/StructureDefinition/"+se.getSd().getName()+"#"+se.getEd().getPath(); @@ -2368,7 +2369,7 @@ private void checkCM(ConceptMap cm, SourcedElementDefinition se, SourcedElementD Coding dc = getCode(dst, tu, tgt.getCode()); if (dc == null) { invalid.add(c); - tgt.setUserData("delete", true); + tgt.setUserData(UserDataNames.xver_delete, true); } else { mapped.add(c); unmapped.remove(dc); @@ -2425,7 +2426,7 @@ private void checkCM(ConceptMap cm, SourcedElementDefinition se, SourcedElementD qaMsg("Concept Map "+cm.getId()+" has invalid mappings to "+toString(invalid), true); if (dst != null) { for (SourceElementComponent e : g.getElement()) { - if (e.getTarget().removeIf(t -> t.hasUserData("delete"))) { + if (e.getTarget().removeIf(t -> t.hasUserData(UserDataNames.xver_delete))) { mod = true; if (e.getTarget().isEmpty()) { e.setNoMap(true); @@ -2525,7 +2526,7 @@ private void checkCM(ConceptMap cm, SourcedElementDefinition se, SourcedElementD private boolean isNotSelectable(Coding c) { - return c.hasUserData("abstract"); + return c.hasUserData(UserDataNames.xver_abstract); } private String removeVersion(String url) { @@ -2621,7 +2622,7 @@ private Set processExpansion(ValueSetExpansionComponent expansion, Strin for (ValueSetExpansionContainsComponent cc : expansion.getContains()) { Coding c = new Coding(injectVersionToUri(cc.getSystem(), version), cc.getVersion(), cc.getCode(), cc.getDisplay()); if (cc.hasAbstract() && cc.getAbstract()) { - c.setUserData("abstract", true); + c.setUserData(UserDataNames.xver_abstract, true); } codes.add(c); } @@ -2769,7 +2770,7 @@ private void buildLinks(XVersions ver, IWorkerContext defsPrev, ConceptMap resFw for (StructureDefinition sd : sortedSDs(defsNext.fetchResourcesByType(StructureDefinition.class))) { if (sd.getKind() == StructureDefinitionKind.COMPLEXTYPE && (!sd.getAbstract() || Utilities.existsInList(sd.getName(), "Quantity")) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { for (ElementDefinition ed : sd.getDifferential().getElement()) { - if (!ed.hasUserData("sed")) { + if (!ed.hasUserData(UserDataNames.xver_sed)) { List links = makeEDLinks(ed, MakeLinkMode.OUTWARD); terminatingElements.add(makeSED(sd, ed)); } @@ -2780,7 +2781,7 @@ private void buildLinks(XVersions ver, IWorkerContext defsPrev, ConceptMap resFw for (StructureDefinition sd : sortedSDs(defsNext.fetchResourcesByType(StructureDefinition.class))) { if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.getAbstract() && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { for (ElementDefinition ed : sd.getDifferential().getElement()) { - if (!ed.hasUserData("sed")) { + if (!ed.hasUserData(UserDataNames.xver_sed)) { List links = makeEDLinks(ed, MakeLinkMode.OUTWARD); terminatingElements.add(makeSED(sd, ed)); } @@ -2833,10 +2834,10 @@ private void buildLinksForElements(XVersions ver, ConceptMap elementFwd, Structu } public SourcedElementDefinition makeSED(StructureDefinition sd, ElementDefinition ed) { - SourcedElementDefinition sed = (SourcedElementDefinition) ed.getUserData("sed"); + SourcedElementDefinition sed = (SourcedElementDefinition) ed.getUserData(UserDataNames.xver_sed); if (sed == null) { sed = new SourcedElementDefinition(sd, ed); - ed.setUserData("sed", sed); + ed.setUserData(UserDataNames.xver_sed, sed); } return sed; } diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/BaseRenderer.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/BaseRenderer.java index c8ec261e8..27b0e17ad 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/BaseRenderer.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/BaseRenderer.java @@ -18,6 +18,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.npm.NpmPackage; @@ -86,7 +87,7 @@ public String processMarkdown(String location, String text) throws FHIRException if (r.hasWebPath()) { url = r.getWebPath(); } else { - url = r.getUserData("filename")+".html"; + url = r.getUserData(UserDataNames.render_filename)+".html"; } } if (Utilities.noString(url)) { diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/CrossViewRenderer.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/CrossViewRenderer.java index 9b41c12c1..85ba3b56b 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/CrossViewRenderer.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/CrossViewRenderer.java @@ -48,6 +48,7 @@ import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; import org.hl7.fhir.r5.utils.ResourceSorters.CanonicalResourceSortByUrl; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.HL7WorkGroups; import org.hl7.fhir.utilities.HL7WorkGroups.HL7WorkGroup; import org.hl7.fhir.utilities.StandardsStatus; @@ -174,7 +175,7 @@ public void seeResource(CanonicalResource res) { private void seeSearchParameter(SearchParameter sp) { try { ExpressionNode n = fpe.parse(sp.getExpression()); - sp.getExpressionElement().setUserData("expression", n); + sp.getExpressionElement().setUserData(UserDataNames.xver_expression, n); } catch (Exception e) { // do nothing in this case } @@ -684,7 +685,7 @@ private void renderCodingCell(StringBuilder b, boolean render, List list if (cs != null) sys = cs.getTitle(); } - t.setUserData("desc", sys); + t.setUserData(UserDataNames.xver_desc, sys); ValidationResult vr = worker.validateCode(ValidationOptions.defaults(), t.getSystem(), t.getVersion(), t.getCode(), null); if (vr != null & vr.getDisplay() != null) { // if (Utilities.existsInList(t.getSystem(), "http://loinc.org")) @@ -1129,7 +1130,7 @@ public String buildSearchTableForExtension(String id) { } else { List list = new ArrayList<>(); for (SearchParameter sp : searchParams) { - ExpressionNode n = (ExpressionNode) sp.getExpressionElement() .getUserData("expression"); + ExpressionNode n = (ExpressionNode) sp.getExpressionElement() .getUserData(UserDataNames.xver_expression); if (n != null && refersToExtension(n, ext.getUrl())) { list.add(sp); } @@ -1277,10 +1278,10 @@ private void resolveVS(List list, String url, Resource source) { if (url != null) { ValueSet vs = context.getContext().findTxResource(ValueSet.class, url); if (vs != null) { - if (!vs.hasUserData("xref.used")) { - vs.setUserData("xref.used", new HashSet<>()); + if (!vs.hasUserData(UserDataNames.pub_xref_used)) { + vs.setUserData(UserDataNames.pub_xref_used, new HashSet<>()); } - Set rl = (Set) vs.getUserData("xref.used"); + Set rl = (Set) vs.getUserData(UserDataNames.pub_xref_used); rl.add(source); if (!list.contains(vs)) { list.add(vs); @@ -1405,7 +1406,7 @@ public String renderVSList(String versionToAnnotate, List vslist, bool td.span(null, "Imports Valueset(s)").tx("V "); } - vs.setUserData("xref.sources", sources); + vs.setUserData(UserDataNames.pub_xref_sources, sources); td = tr.td(); for (String s : Utilities.sorted(sources)) { td.sep(", "); @@ -1413,7 +1414,7 @@ public String renderVSList(String versionToAnnotate, List vslist, bool } if (used) { td = tr.td(); - Set rl = (Set) vs.getUserData("xref.used"); + Set rl = (Set) vs.getUserData(UserDataNames.pub_xref_used); if (rl != null) { if (rl.size() < 10) { for (Resource r : rl) { @@ -1597,10 +1598,10 @@ private void resolveCS(List list, String url, Resource source) { if (url != null) { CodeSystem cs = context.getContext().fetchResource(CodeSystem.class, url); if (cs != null) { - if (!cs.hasUserData("xref.used")) { - cs.setUserData("xref.used", new HashSet<>()); + if (!cs.hasUserData(UserDataNames.pub_xref_used)) { + cs.setUserData(UserDataNames.pub_xref_used, new HashSet<>()); } - Set rl = (Set) cs.getUserData("xref.used"); + Set rl = (Set) cs.getUserData(UserDataNames.pub_xref_used); rl.add(source); if (!list.contains(cs)) { list.add(cs); @@ -1683,7 +1684,7 @@ public String renderCSList(String versionToAnnotate, List cslist, bo } if (used) { td = tr.td(); - Set rl = (Set) cs.getUserData("xref.used"); + Set rl = (Set) cs.getUserData(UserDataNames.pub_xref_used); if (rl != null) { if (rl.size() < 10) { for (Resource r : rl) { diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DependencyRenderer.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DependencyRenderer.java index 985e0483d..2e341f4a9 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DependencyRenderer.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DependencyRenderer.java @@ -29,6 +29,7 @@ import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; @@ -282,7 +283,7 @@ private void addPackageRow(HierarchicalTableGenerator gen, List rows, NpmPa b2.append("\r\n

Dependencies

\r\n"); boolean first = true; for (ArtifactDependency ad : dependencies) { - String t = ad.getTarget().getUserString("package"); + String t = ad.getTarget().getUserString(UserDataNames.render_src_package); // todo: this isn't ever set anywhere? if (n.equals(t)) { if (first) { b2.append("
    \r\n"); diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureMapRenderer.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureMapRenderer.java index 574eb2e08..744ac3f87 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureMapRenderer.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureMapRenderer.java @@ -36,6 +36,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; import org.hl7.fhir.r5.utils.EOperationOutcome; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.r5.utils.structuremap.StructureMapAnalysis; import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; import org.hl7.fhir.utilities.MarkDownProcessor; @@ -58,7 +59,7 @@ public StructureMapRenderer(IWorkerContext context, String corePath, StructureMa this.map = map; this.destDir = destDir; utils = new StructureMapUtilities(context, null, igp); - analysis = (StructureMapAnalysis) map.getUserData("analysis"); + analysis = (StructureMapAnalysis) map.getUserData(UserDataNames.pub_analysis); } @Override @@ -96,7 +97,7 @@ public String content() throws IOException { } catch (FHIRException e) { return "Error in Map: "+e.getMessage(); } - map.setUserData("analysis", analysis); + map.setUserData(UserDataNames.pub_analysis, analysis); } XhtmlNode summary = analysis.getSummary(); return summary == null ? "" : new XhtmlComposer(XhtmlComposer.HTML).compose(summary); diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/ValueSetRenderer.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/ValueSetRenderer.java index 0583793e7..b705b8059 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/ValueSetRenderer.java @@ -54,6 +54,7 @@ import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; import org.hl7.fhir.r5.terminologies.ValueSetUtilities; import org.hl7.fhir.r5.utils.EOperationOutcome; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.npm.NpmPackage; @@ -80,7 +81,7 @@ protected void genSummaryRowsSpecific(StringBuilder b, Set rows) { } public String cld(Set outputTracker) throws EOperationOutcome, FHIRException, IOException, org.hl7.fhir.exceptions.FHIRException { - if (vs.hasText() && !vs.getText().hasUserData("renderer.generated") && vs.getText().hasDiv()) { + if (vs.hasText() && !vs.getText().hasUserData(UserDataNames.renderer_is_generated) && vs.getText().hasDiv()) { for (XhtmlNode n : vs.getText().getDiv().getChildNodes()) { if ("div".equals(n.getName()) && "cld".equals(n.getAttribute("id"))) { return "

    Definition

    \r\n" + new XhtmlComposer(XhtmlComposer.HTML).compose(n); diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/CodeSystemConvertor.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/CodeSystemConvertor.java index 29c0cbad6..910c767c1 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/CodeSystemConvertor.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/CodeSystemConvertor.java @@ -33,6 +33,7 @@ import org.hl7.fhir.r5.model.UsageContext; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.Utilities; public class CodeSystemConvertor { @@ -67,16 +68,16 @@ public static void populate(CodeSystem cs, ValueSet vs) { if (!vs.hasDescription()) throw new Error("No description vs "+vs.getUrl()); - if (cs.getUserData("conv-vs") != null) + if (cs.getUserData(UserDataNames.PUB_CS_CONVERTED) != null) throw new Error("This code system has already been converted"); - cs.setUserData("conv-vs", "done"); - vs.setUserData("cs", cs); - if (vs.hasUserData("filename")) - cs.setUserData("filename", vs.getUserString("filename").replace("valueset-", "codesystem-")); + cs.setUserData(UserDataNames.PUB_CS_CONVERTED, "done"); + vs.setUserData(UserDataNames.TX_ASSOCIATED_CODESYSTEM, cs); + if (vs.hasUserData(UserDataNames.render_filename)) + cs.setUserData(UserDataNames.render_filename, vs.getUserString(UserDataNames.render_filename).replace("valueset-", "codesystem-")); if (vs.hasWebPath()) cs.setWebPath(vs.getWebPath().replace("valueset-", "codesystem-")); - if (vs.hasUserData("committee")) - cs.setUserData("committee", vs.getUserData("committee")); + if (vs.hasUserData(UserDataNames.deprecated_committee)) + cs.setUserData(UserDataNames.deprecated_committee, vs.getUserData(UserDataNames.deprecated_committee)); cs.setId(vs.getId()); cs.setVersion(vs.getVersion()); cs.setName(vs.getName()); diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/IgSpreadsheetParser.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/IgSpreadsheetParser.java index 21194370c..b41dd4d1c 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/IgSpreadsheetParser.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/IgSpreadsheetParser.java @@ -103,6 +103,7 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.ValueSetUtilities; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -345,10 +346,10 @@ else if (sheet.hasColumn(0, "Profile.name")) if (invariants != null) { for (ElementDefinitionConstraintComponent inv : invariants.values()) { - if (Utilities.noString(inv.getUserString("context"))) + if (Utilities.noString(inv.getUserString(UserDataNames.pub_excel_inv_context))) throw new Exception("Profile "+sd.getId()+" Invariant "+inv.getId()+" has no context"); else { - ElementDefinition ed = findContext(sd, inv.getUserString("context"), "Profile "+sd.getId()+" Invariant "+inv.getId()+" Context"); + ElementDefinition ed = findContext(sd, inv.getUserString(UserDataNames.pub_excel_inv_context), "Profile "+sd.getId()+" Invariant "+inv.getId()+" Context"); ed.getConstraint().add(inv); // if (Utilities.noString(inv.getXpath())) { // throw new Exception("Profile "+sd.getId()+" Invariant "+inv.getId()+" ("+inv.getHuman()+") has no XPath statement"); @@ -589,12 +590,12 @@ private void loadValueSet(ValueSet vs, Sheet sheet, String sheetName) throws Exc if (Utilities.noString(sheet.getColumn(row, "System"))) { ConceptDefinitionComponent cc = new ConceptDefinitionComponent(); - cc.setUserData("id", sheet.getColumn(row, "Id")); + cc.setUserData(UserDataNames.pub_excel_sheet_id, sheet.getColumn(row, "Id")); cc.setCode(sheet.getColumn(row, "Code")); if (codes.containsKey(cc.getCode())) throw new Exception("Duplicate Code '"+cc.getCode()+"' processing "+vs.getName()); codes.put(cc.getCode(), cc); - codesById.put(cc.getUserString("id"), cc); + codesById.put(cc.getUserString(UserDataNames.pub_excel_sheet_id), cc); cc.setDisplay(sheet.getColumn(row, "Display")); if (sheet.getColumn(row, "Abstract").toUpperCase().equals("Y")) CodeSystemUtilities.setNotSelectable(cs, cc); @@ -603,8 +604,8 @@ private void loadValueSet(ValueSet vs, Sheet sheet, String sheetName) throws Exc cc.setDefinition(sheet.getColumn(row, "Definition")); if (!Utilities.noString(sheet.getColumn(row, "Comment"))) ToolingExtensions.addCSComment(cc, sheet.getColumn(row, "Comment")); -// cc.setUserData("v2", sheet.getColumn(row, "v2")); -// cc.setUserData("v3", sheet.getColumn(row, "v3")); +// cc.setUserData(!"v2", sheet.getColumn(row, "v2")); +// cc.setUserData(!"v3", sheet.getColumn(row, "v3")); for (String ct : sheet.columns) if (ct.startsWith("Display:") && !Utilities.noString(sheet.getColumn(row, ct))) cc.addDesignation().setLanguage(ct.substring(8)).setValue(sheet.getColumn(row, ct)); @@ -687,7 +688,7 @@ private Map readInvariants(Sheet sh // inv.setXpath(sheet.getColumn(row, "XPath")); if (s.equals("") || result.containsKey(s)) throw new Exception("duplicate or missing invariant id "+ getLocation(row)); - inv.setUserData("context", sheet.getColumn(row, "Context")); + inv.setUserData(UserDataNames.pub_excel_inv_context, sheet.getColumn(row, "Context")); result.put(s, inv); } } @@ -780,15 +781,15 @@ private ElementDefinition processLine(StructureDefinition sd, Sheet sheet, int r if (!Utilities.noString(uml)) { if (uml.contains(";")) { String[] parts = uml.split("\\;"); - e.setUserData("SvgLeft", parts[0]); - e.setUserData("SvgTop", parts[1]); + e.setUserData(UserDataNames.LAYOUT_SvgLeft, parts[0]); + e.setUserData(UserDataNames.LAYOUT_SvgTop, parts[1]); if (parts.length > 2) - e.setUserData("SvgWidth", parts[2]); + e.setUserData(UserDataNames.LAYOUT_SvgWidth, parts[2]); } else if (uml.startsWith("break:")) { - e.setUserData("UmlBreak", true); - e.setUserData("UmlDir", uml.substring(6)); + e.setUserData(UserDataNames.LAYOUT_UmlBreak, true); + e.setUserData(UserDataNames.LAYOUT_UmlDir, uml.substring(6)); } else { - e.setUserData("UmlDir", uml); + e.setUserData(UserDataNames.LAYOUT_UmlDir, uml); } } String s = sheet.getColumn(row, "Condition"); diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/ObservationSummarySpreadsheetGenerator.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/ObservationSummarySpreadsheetGenerator.java index 893803c1a..367f3a242 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/ObservationSummarySpreadsheetGenerator.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/spreadsheets/ObservationSummarySpreadsheetGenerator.java @@ -10,6 +10,7 @@ import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.renderers.spreadsheets.SpreadsheetGenerator; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; public class ObservationSummarySpreadsheetGenerator extends SpreadsheetGenerator { @@ -71,7 +72,7 @@ public ObservationSummarySpreadsheetGenerator generate(List private String renderCodes(List codes) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(", "); for (Coding t : codes) { - String sys = t.getUserString("desc"); + String sys = t.getUserString(UserDataNames.xver_desc); b.append(sys+"#"+t.getCode()); } return b.toString(); From 683912ad40fd06f3bc1c5f07a3c45bf843ae2af2 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 18 Nov 2024 07:14:00 +1100 Subject: [PATCH 2/3] Add support for custom resources --- .../igtools/publisher/FetchedResource.java | 19 + .../hl7/fhir/igtools/publisher/Publisher.java | 422 ++++++++++++------ .../hl7/fhir/igtools/renderers/DBBuilder.java | 117 ++--- .../StructureDefinitionRenderer.java | 45 +- pom.xml | 2 +- 5 files changed, 403 insertions(+), 202 deletions(-) diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FetchedResource.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FetchedResource.java index 1f60b1be4..5807416fd 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FetchedResource.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FetchedResource.java @@ -27,6 +27,7 @@ import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.ImplementationGuide.ImplementationGuideDefinitionResourceComponent; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.utilities.json.model.JsonObject; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -60,6 +61,7 @@ public class FetchedResource { private boolean hasTranslations; private String resourceName; private String resourceDescription; + private boolean regenAfterValidation; public FetchedResource(String nameForErrors) { super(); @@ -352,5 +354,22 @@ public String getBestName() { return resourceDescription; } } + + public boolean isRegenAfterValidation() { + return regenAfterValidation; + } + + public void setRegenAfterValidation(boolean regenAfterValidation) { + this.regenAfterValidation = regenAfterValidation; + } + + public boolean isCustomResource() { + if (getResource() != null) { + return getResource().hasUserData(UserDataNames.loader_custom_resource); + } else { + return getElement().getProperty().getStructure().hasUserData(UserDataNames.loader_custom_resource); + } + } + } diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java index 50fcccdad..546d50b36 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java @@ -276,6 +276,7 @@ import org.hl7.fhir.r5.renderers.DataRenderer; import org.hl7.fhir.r5.renderers.ParametersRenderer; import org.hl7.fhir.r5.renderers.RendererFactory; +import org.hl7.fhir.r5.renderers.ResourceRenderer; import org.hl7.fhir.r5.renderers.spreadsheets.CodeSystemSpreadsheetGenerator; import org.hl7.fhir.r5.renderers.spreadsheets.ConceptMapSpreadsheetGenerator; import org.hl7.fhir.r5.renderers.spreadsheets.StructureDefinitionSpreadsheetGenerator; @@ -307,6 +308,7 @@ import org.hl7.fhir.r5.utils.ResourceSorters; import org.hl7.fhir.r5.utils.ResourceUtilities; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.client.FHIRToolingClient; import org.hl7.fhir.r5.utils.formats.CSVWriter; @@ -314,6 +316,7 @@ import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidationProfileUsageTracker; +import org.hl7.fhir.r5.utils.validation.ValidatorSession; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.DurationUtil; import org.hl7.fhir.utilities.FhirPublication; @@ -335,6 +338,7 @@ import org.hl7.fhir.utilities.i18n.LanguageFileProducer.TranslationUnit; import org.hl7.fhir.utilities.i18n.subtag.LanguageSubtagRegistry; import org.hl7.fhir.utilities.i18n.subtag.LanguageSubtagRegistryLoader; +import org.hl7.fhir.utilities.json.JsonException; import org.hl7.fhir.utilities.json.model.JsonArray; import org.hl7.fhir.utilities.json.model.JsonBoolean; import org.hl7.fhir.utilities.json.model.JsonElement; @@ -763,6 +767,7 @@ public enum CacheOption { private final List noNarratives = new ArrayList<>(); private List noNarrativeResources = new ArrayList<>(); private final List noValidate = new ArrayList<>(); + private final List customResourceFiles = new ArrayList<>(); private List noValidateResources = new ArrayList<>(); private List resourceDirs = new ArrayList(); @@ -1234,6 +1239,11 @@ public void createIg() throws Exception, IOException, EOperationOutcome, FHIRExc log("Unhandled Exception: " +ex.toString()); throw(ex); } + validatorSession.close(); + } + if (needsRegen) { + log("Regenerating Narratives"); + generateNarratives(true); } log("Processing Provenance Records"); processProvenanceDetails(); @@ -2158,87 +2168,104 @@ public List getDescendantExtensions(Base e, String url) { return extensions; } - private void generateNarratives() throws Exception { + private void generateNarratives(boolean isRegen) throws Exception { Session tts = tt.start("narrative generation"); - logDebugMessage(LogCategory.PROGRESS, "gen narratives"); + logDebugMessage(LogCategory.PROGRESS, isRegen ? "regen narratives" : "gen narratives"); for (FetchedFile f : fileList) { f.start("generateNarratives"); try { for (FetchedResource r : f.getResources()) { - if (r.getExampleUri()==null || genExampleNarratives) { - if (!passesNarrativeFilter(r)) { - noNarrativeResources.add(r); - logDebugMessage(LogCategory.PROGRESS, "narrative for "+f.getName()+" : "+r.getId()+" suppressed"); - if (r.getResource() != null && r.getResource() instanceof DomainResource) { - ((DomainResource) r.getResource()).setText(null); - } - r.getElement().removeChild("text"); - } else { - List langs = translationLocales(); - logDebugMessage(LogCategory.PROGRESS, "narrative for "+f.getName()+" : "+r.getId()); - if (r.getResource() != null && isConvertableResource(r.getResource().fhirType())) { - boolean regen = false; - for (Locale lang : langs) { - boolean first = true; - RenderingContext lrc = rc.copy(false).setDefinitionsTarget(igpkp.getDefinitionsName(r)); - lrc.setLocale(lang); - lrc.setRules(GenerationRules.VALID_RESOURCE); - lrc.setDefinitionsTarget(igpkp.getDefinitionsName(r)); - lrc.setSecondaryLang(!first); - first = false; - if (r.getResource() instanceof DomainResource && (langs.size() > 1 || !(((DomainResource) r.getResource()).hasText() && ((DomainResource) r.getResource()).getText().hasDiv()))) { - regen = true; - RendererFactory.factory(r.getResource(), lrc).setMultiLangMode(langs.size() > 1).renderResource(ResourceWrapper.forResource(lrc, r.getResource())); - } else if (r.getResource() instanceof Bundle) { - regen = true; - new BundleRenderer(lrc).setMultiLangMode(langs.size() > 1).renderResource(ResourceWrapper.forResource(lrc, r.getResource())); - } else if (r.getResource() instanceof Parameters) { - regen = true; - Parameters p = (Parameters) r.getResource(); - new ParametersRenderer(lrc).setMultiLangMode(langs.size() > 1).renderResource(ResourceWrapper.forResource(lrc, p)); - } else if (r.getResource() instanceof DomainResource) { - checkExistingNarrative(f, r, ((DomainResource) r.getResource()).getText().getDiv()); - } - } - if (regen) { - Element e = convertToElement(r, r.getResource()); - e.copyUserData(r.getElement()); - r.setElement(e); + if (!isRegen || r.isRegenAfterValidation()) { + if (r.getExampleUri()==null || genExampleNarratives) { + if (!passesNarrativeFilter(r)) { + noNarrativeResources.add(r); + logDebugMessage(LogCategory.PROGRESS, "narrative for "+f.getName()+" : "+r.getId()+" suppressed"); + if (r.getResource() != null && r.getResource() instanceof DomainResource) { + ((DomainResource) r.getResource()).setText(null); } + r.getElement().removeChild("text"); } else { - boolean first = true; - for (Locale lang : langs) { - RenderingContext lrc = rc.copy(false).setParser(getTypeLoader(f,r)); - lrc.clearAnchors(); - lrc.setLocale(lang); - lrc.setRules(GenerationRules.VALID_RESOURCE); - lrc.setSecondaryLang(!first); - first = false; - if (isDomainResource(r) && (langs.size() > 1|| !hasNarrative(r.getElement()))) { - ResourceWrapper rw = ResourceWrapper.forResource(lrc, r.getElement()); - RendererFactory.factory(rw, lrc).setMultiLangMode(langs.size() > 1).renderResource(rw); - otherFilesRun.addAll(lrc.getFiles()); - } else if (r.fhirType().equals("Bundle")) { - lrc.setAddName(true); - for (Element e : r.getElement().getChildrenByName("entry")) { - Element res = e.getNamedChild("resource"); - if (res!=null && "http://hl7.org/fhir/StructureDefinition/DomainResource".equals(res.getProperty().getStructure().getBaseDefinition())) { - ResourceWrapper rw = ResourceWrapper.forResource(lrc, res); - if (hasNarrative(res)) { - RendererFactory.factory(rw, lrc).checkNarrative(rw); - } else { - RendererFactory.factory(rw, lrc).setMultiLangMode(langs.size() > 1).renderResource(rw); + List langs = translationLocales(); + logDebugMessage(LogCategory.PROGRESS, "narrative for "+f.getName()+" : "+r.getId()); + if (r.getResource() != null && isConvertableResource(r.getResource().fhirType())) { + boolean regen = false; + for (Locale lang : langs) { + boolean first = true; + RenderingContext lrc = rc.copy(false).setDefinitionsTarget(igpkp.getDefinitionsName(r)); + lrc.setLocale(lang); + lrc.setRules(GenerationRules.VALID_RESOURCE); + lrc.setDefinitionsTarget(igpkp.getDefinitionsName(r)); + lrc.setSecondaryLang(!first); + first = false; + if (r.getResource() instanceof DomainResource && (langs.size() > 1 || !(((DomainResource) r.getResource()).hasText() && ((DomainResource) r.getResource()).getText().hasDiv()))) { + regen = true; + ResourceRenderer rr = RendererFactory.factory(r.getResource(), lrc); + if (rr.renderingUsesValidation()) { + r.setRegenAfterValidation(true); + needsRegen = true; + } + rr.setMultiLangMode(langs.size() > 1).renderResource(ResourceWrapper.forResource(lrc, r.getResource())); + } else if (r.getResource() instanceof Bundle) { + regen = true; + new BundleRenderer(lrc).setMultiLangMode(langs.size() > 1).renderResource(ResourceWrapper.forResource(lrc, r.getResource())); + } else if (r.getResource() instanceof Parameters) { + regen = true; + Parameters p = (Parameters) r.getResource(); + new ParametersRenderer(lrc).setMultiLangMode(langs.size() > 1).renderResource(ResourceWrapper.forResource(lrc, p)); + } else if (r.getResource() instanceof DomainResource) { + checkExistingNarrative(f, r, ((DomainResource) r.getResource()).getText().getDiv()); + } + } + if (regen) { + Element e = convertToElement(r, r.getResource()); + e.copyUserData(r.getElement()); + r.setElement(e); + } + } else { + boolean first = true; + for (Locale lang : langs) { + RenderingContext lrc = rc.copy(false).setParser(getTypeLoader(f,r)); + lrc.clearAnchors(); + lrc.setLocale(lang); + lrc.setRules(GenerationRules.VALID_RESOURCE); + lrc.setSecondaryLang(!first); + first = false; + if (isDomainResource(r) && (isRegen || langs.size() > 1 || !hasNarrative(r.getElement()))) { + ResourceWrapper rw = ResourceWrapper.forResource(lrc, r.getElement()); + ResourceRenderer rr = RendererFactory.factory(rw, lrc); + if (rr.renderingUsesValidation()) { + r.setRegenAfterValidation(true); + needsRegen = true; + } + rr.setMultiLangMode(langs.size() > 1).renderResource(rw); + otherFilesRun.addAll(lrc.getFiles()); + } else if (r.fhirType().equals("Bundle")) { + lrc.setAddName(true); + for (Element e : r.getElement().getChildrenByName("entry")) { + Element res = e.getNamedChild("resource"); + if (res!=null && "http://hl7.org/fhir/StructureDefinition/DomainResource".equals(res.getProperty().getStructure().getBaseDefinition())) { + ResourceWrapper rw = ResourceWrapper.forResource(lrc, res); + ResourceRenderer rr = RendererFactory.factory(rw, lrc); + if (rr.renderingUsesValidation()) { + r.setRegenAfterValidation(true); + needsRegen = true; + } + if (hasNarrative(res)) { + rr.checkNarrative(rw); + } else { + rr.setMultiLangMode(langs.size() > 1).renderResource(rw); + } } } + } else if (isDomainResource(r) && hasNarrative(r.getElement())) { + checkExistingNarrative(f, r, r.getElement().getNamedChild("text").getNamedChild("div").getXhtml()); } - } else if (isDomainResource(r) && hasNarrative(r.getElement())) { - checkExistingNarrative(f, r, r.getElement().getNamedChild("text").getNamedChild("div").getXhtml()); } } } + } else { + logDebugMessage(LogCategory.PROGRESS, "skipped narrative for "+f.getName()+" : "+r.getId()); } - } else { - logDebugMessage(LogCategory.PROGRESS, "skipped narrative for "+f.getName()+" : "+r.getId()); } } } finally { @@ -3043,6 +3070,9 @@ private void initializeFromIg(IniFile ini) throws Exception { case "conversion-version": conversionVersions.add(p.getValue()); break; + case "custom-resource": + customResourceFiles.add(p.getValue()); + break; case "suppressed-ids": for (String s1 : p.getValue().split("\\,")) suppressedIds.add(s1); @@ -3325,7 +3355,7 @@ else if (vsCache == null) { if (VersionUtilities.isR4Plus(version) && !dependsOnExtensions(sourceIg.getDependsOn()) && !sourceIg.getPackageId().contains("hl7.fhir.uv.extensions")) { ImplementationGuideDependsOnComponent dep = new ImplementationGuideDependsOnComponent(); - dep.setUserData("no-load-deps", "true"); + dep.setUserData(UserDataNames.pub_no_load_deps, "true"); dep.setId("hl7ext"); dep.setPackageId(getExtensionsPackageName()); dep.setUri("http://hl7.org/fhir/extensions/ImplementationGuide/hl7.fhir.uv.extensions"); @@ -3335,7 +3365,7 @@ else if (vsCache == null) { } if (!dependsOnUTG(sourceIg.getDependsOn()) && !sourceIg.getPackageId().contains("hl7.terminology")) { ImplementationGuideDependsOnComponent dep = new ImplementationGuideDependsOnComponent(); - dep.setUserData("no-load-deps", "true"); + dep.setUserData(UserDataNames.pub_no_load_deps, "true"); dep.setId("hl7tx"); dep.setPackageId(getUTGPackageName()); dep.setUri("http://terminology.hl7.org/ImplementationGuide/hl7.terminology"); @@ -3365,7 +3395,7 @@ else if (vsCache == null) { int i = 0; for (ImplementationGuideDependsOnComponent dep : sourceIg.getDependsOn()) { - loadIg(dep, i, !dep.hasUserData("no-load-deps")); + loadIg(dep, i, !dep.hasUserData(UserDataNames.pub_no_load_deps)); i++; } if (!"hl7.fhir.uv.tools".equals(sourceIg.getPackageId()) && !dependsOnTooling(sourceIg.getDependsOn())) { @@ -3398,7 +3428,8 @@ else if (vsCache == null) { generateLoadedSnapshots(); // set up validator; - validator = new InstanceValidator(context, new IGPublisherHostServices(), context.getXVer()); // todo: host services for reference resolution.... + validatorSession = new ValidatorSession(); + validator = new InstanceValidator(context, new IGPublisherHostServices(), context.getXVer(), validatorSession); // todo: host services for reference resolution.... validator.setAllowXsiLocation(true); validator.setNoBindingMsgSuppressed(true); validator.setNoExtensibleWarnings(!allowExtensibleWarnings); @@ -3411,8 +3442,8 @@ else if (vsCache == null) { validator.setDisplayWarnings(displayWarnings); cu = new ContextUtilities(context); - pvalidator = new ProfileValidator(context, context.getXVer()); - csvalidator = new CodeSystemValidator(context, context.getXVer()); + pvalidator = new ProfileValidator(context, context.getXVer(), validatorSession); + csvalidator = new CodeSystemValidator(context, context.getXVer(), validatorSession); pvalidator.setCheckAggregation(checkAggregation); pvalidator.setCheckMustSupport(hintAboutNonMustSupport); validator.setShowMessagesFromReferences(showReferenceMessages); @@ -3573,9 +3604,9 @@ private void generateSnapshot(StructureDefinition sd) { System.out.println("Exception generating snapshot for "+sd.getUrl()+": "+e.getMessage()); } } - Element element = (Element) sd.getUserData("element"); + Element element = (Element) sd.getUserData(UserDataNames.pub_element); if (element != null) { - element.setUserData("profileutils.snapshot.messages", messages); + element.setUserData(UserDataNames.SNAPSHOT_messages, messages); } } @@ -4517,7 +4548,9 @@ private boolean load() throws Exception { fetcher.setRootDir(rootDir); loadedIds = new HashMap<>(); duplicateInputResourcesDetected = false; - // load any bundles + + loadCustomResources(); + if (sourceDir != null || igpkp.isAutoPath()) needToBuild = loadResources(needToBuild, igf); needToBuild = loadSpreadsheets(needToBuild, igf); @@ -4538,7 +4571,7 @@ private boolean load() throws Exception { } i++; FetchedFile f = null; - if (!bndIds.contains(res.getReference().getReference()) && !res.hasUserData("loaded.resource")) { + if (!bndIds.contains(res.getReference().getReference()) && !res.hasUserData(UserDataNames.pub_loaded_resource)) { logDebugMessage(LogCategory.INIT, "Load "+res.getReference()); f = fetcher.fetch(res.getReference(), igf); if (!f.hasTitle() && res.getName() != null) @@ -4561,7 +4594,7 @@ private boolean load() throws Exception { if (res.hasProfile()) { if (f != null && f.getResources().size()!=1) throw new Exception("Can't have an exampleFor unless the file has exactly one resource"); - FetchedResource r = res.hasUserData("loaded.resource") ? (FetchedResource) res.getUserData("loaded.resource") : f.getResources().get(0); + FetchedResource r = res.hasUserData(UserDataNames.pub_loaded_resource) ? (FetchedResource) res.getUserData(UserDataNames.pub_loaded_resource) : f.getResources().get(0); if (r == null) throw new Exception("Unable to resolve example canonical " + res.getProfile().get(0).asStringValue()); examples.add(r); @@ -4581,7 +4614,7 @@ private boolean load() throws Exception { f = fetcher.fetch(res.getReference(), igf); } if (f != null) { - FetchedResource r = res.hasUserData("loaded.resource") ? (FetchedResource) res.getUserData("loaded.resource") : f.getResources().get(0); + FetchedResource r = res.hasUserData(UserDataNames.pub_loaded_resource) ? (FetchedResource) res.getUserData(UserDataNames.pub_loaded_resource) : f.getResources().get(0); if (r != null) { testplans.add(r); try { @@ -4616,7 +4649,7 @@ private boolean load() throws Exception { f = fetcher.fetch(res.getReference(), igf); } if (f != null) { - FetchedResource r = res.hasUserData("loaded.resource") ? (FetchedResource) res.getUserData("loaded.resource") : f.getResources().get(0); + FetchedResource r = res.hasUserData(UserDataNames.pub_loaded_resource) ? (FetchedResource) res.getUserData(UserDataNames.pub_loaded_resource) : f.getResources().get(0); if (r != null) { testscripts.add(r); try { @@ -4776,7 +4809,7 @@ private boolean load() throws Exception { // sanity check: every specified resource must be loaded, every loaded resource must be specified for (ImplementationGuideDefinitionResourceComponent r : publishedIg.getDefinition().getResource()) { b.append(r.getReference().getReference()); - if (!r.hasUserData("loaded.resource")) { + if (!r.hasUserData(UserDataNames.pub_loaded_resource)) { log("Resource "+r.getReference().getReference()+" not loaded"); failed = true; } @@ -4898,10 +4931,119 @@ private boolean load() throws Exception { } extensionTracker.scan(publishedIg); - + finishLoadingCustomResources(); return needToBuild; } + private void finishLoadingCustomResources() { + for (StructureDefinition sd : customResources) { + FetchedResource r = findLoadedStructure(sd); + if (r == null) { + System.out.println("Custom Resource "+sd.getId()+" not loaded normally"); + System.exit(1); + } else { + sd.setWebPath(igpkp.getDefinitionsName(r)); + // also mark this as a custom resource + r.getResource().setUserData(UserDataNames.loader_custom_resource, "true"); + } + } + } + + private FetchedResource findLoadedStructure(StructureDefinition sd) { + for (var f : fileList) { + for (var r : f.getResources()) { + if (r.fhirType().equals("StructureDefinition") && r.getId().equals(sd.getId())) { + return r; + } + } + } + return null; + } + + /** + * this has to be called before load, and then load will reload the resource and override; + * @throws IOException + * @throws FHIRException + * @throws FileNotFoundException + * + */ + private void loadCustomResources() throws FileNotFoundException, FHIRException, IOException { + for (String s : customResourceFiles) { + System.out.print("Load Custom Resource from "+s+":"); + System.out.println(loadCustomResource(s)); + } + } + + /** + * The point of this routine is to load the source file, and get the definition of the resource into the context + * before anything else is loaded. The resource must be loaded normally for processing etc - we'll check that it has been later + * @param filename + * @throws IOException + * @throws FHIRException + * @throws FileNotFoundException + */ + private String loadCustomResource(String filename) throws FileNotFoundException, FHIRException, IOException { + // we load it as an R5 resource. + StructureDefinition def = null; + try { + def = (StructureDefinition) org.hl7.fhir.r5.formats.FormatUtilities.loadFile(Utilities.uncheckedPath(Utilities.getDirectoryForFile(configFile), filename)); + } catch (Exception e) { + return "Exception loading: "+e.getMessage(); + } + + if (approvedIgsForCustomResources == null) { + try { + approvedIgsForCustomResources = org.hl7.fhir.utilities.json.parser.JsonParser.parseObjectFromUrl("https://fhir.github.io/ig-registry/igs-approved-for-custom-resource.json"); + } catch (Exception e) { + approvedIgsForCustomResources = new JsonObject(); + return "Exception checking IG status: "+e.getMessage(); + } + } + // checks + // we'll validate it properly later. For now, we want to know: + // 1. is this IG authorized to define custom resources? + if (!approvedIgsForCustomResources.asBoolean(npmName)) { + return "This IG is not authorised to define custom resources"; + } + // 2. is this in the namespace of the IG (no flex there) + if (!def.getUrl().startsWith(igpkp.getCanonical())) { + return "The URL of this definition is not in the proper canonical URL space of the IG ("+igpkp.getCanonical()+")"; + } + // 3. is this based on Resource or DomainResource + if (!Utilities.existsInList(def.getBaseDefinition(), + "http://hl7.org/fhir/StructureDefinition/Resource", + "http://hl7.org/fhir/StructureDefinition/DomainResource", + "http://hl7.org/fhir/StructureDefinition/CanonicalResource", + "http://hl7.org/fhir/StructureDefinition/MetadataResource")) { + return "The definition must be based on Resource, DomainResource, CanonicalResource, or MetadataResource"; + } +// // 4. is this active? (this is an easy way to turn this off if it stops the IG from building +// if (def.getStatus() != PublicationStatus.ACTIVE) { +// return "The definition is not active, so ignored"; +// } + // 5. is this a specialization + if (def.getDerivation() == TypeDerivationRule.CONSTRAINT) { + return "This definition is not a specialization, so ignored"; + } + + if (def.getKind() == StructureDefinitionKind.LOGICAL) { + def.setKind(StructureDefinitionKind.RESOURCE); + } + if (def.getKind() != StructureDefinitionKind.RESOURCE) { + return "This definition does not describe a resource"; + } + if (def.getType().contains(":/")) { + def.setType(tail(def.getType())); + } + // right, passed all the tests + customResourceNames.add(def.getType()); + customResources.add(def); + def.setUserData(UserDataNames.loader_custom_resource, "true"); + def.setWebPath("placeholder.html"); // we'll figure it out later + context.cacheResource(def); + return "loaded"; + } + private boolean loadTranslationSupplements(boolean needToBuild, FetchedFile igf) throws Exception { for (String p : translationSources) { File dir = new File(Utilities.path(rootDir, p)); @@ -4953,7 +5095,7 @@ private boolean loadTranslationSupplement(File f, boolean needToBuild) throws Ex // ok good to go CodeSystem csSrc = makeSupplement(cr, true); // what could be translated CodeSystem csDst = makeSupplement(cr, false); // what has been translated - csDst.setUserData("source.filename", f.getName().substring(0, f.getName().indexOf("."))); + csDst.setUserData(UserDataNames.pub_source_filename, f.getName().substring(0, f.getName().indexOf("."))); List list = loadTranslations(f, ext); langUtils.fillSupplement(csSrc, csDst, list); FetchedResource rr = ff.addResource("CodeSystemSupplement"); @@ -4973,7 +5115,7 @@ private boolean loadTranslationSupplement(File f, boolean needToBuild) throws Ex } res.setReference(new Reference().setReference(r.fhirType()+"/"+r.getId())); } - res.setUserData("loaded.resource", r); + res.setUserData(UserDataNames.pub_loaded_resource, r); r.setResEntry(res); } return changed; @@ -5453,7 +5595,7 @@ private boolean loadBundle(String name, boolean needToBuild, FetchedFile igf, St } res.setReference(new Reference().setReference(r.fhirType()+"/"+r.getId())); } - res.setUserData("loaded.resource", r); + res.setUserData(UserDataNames.pub_loaded_resource, r); r.setResEntry(res); if (r.getResource() instanceof CanonicalResource) { CanonicalResource cr = (CanonicalResource)r.getResource(); @@ -5512,7 +5654,7 @@ private boolean loadResource(boolean needToBuild, FetchedFile f, String cause) t res.setDescription(((CanonicalResource)r.getResource()).getDescription().trim()); res.setReference(new Reference().setReference(r.fhirType()+"/"+r.getId())); } - res.setUserData("loaded.resource", r); + res.setUserData(UserDataNames.pub_loaded_resource, r); r.setResEntry(res); } return changed || needToBuild; @@ -5628,7 +5770,7 @@ private boolean loadSpreadsheet(String name, boolean needToBuild, FetchedFile ig } res.setReference(new Reference().setReference(r.fhirType()+"/"+r.getId())); } - res.setUserData("loaded.resource", r); + res.setUserData(UserDataNames.pub_loaded_resource, r); r.setResEntry(res); } return changed || needToBuild; @@ -5730,7 +5872,7 @@ private void loadConformance() throws Exception { propagateStatus(); } log("Generating Narratives"); - generateNarratives(); + generateNarratives(false); if (!validationOff) { log("Validating Conformance Resources"); for (String s : metadataResourceNames()) { @@ -5757,9 +5899,9 @@ private void assignComparisonIds() { StructureDefinition sd = (StructureDefinition) r.getResource(); for (Extension ext : sd.getExtensionsByUrl(ToolingExtensions.EXT_SD_IMPOSE_PROFILE)) { StructureDefinition sdi = context.fetchResource(StructureDefinition.class, ext.getValue().primitiveValue()); - if (sdi != null && !sdi.hasUserData("imposes.compare.id")) { + if (sdi != null && !sdi.hasUserData(UserDataNames.pub_imposes_compare_id)) { String cid = "c"+Integer.toString(i); - sdi.setUserData("imposes.compare.id", cid); + sdi.setUserData(UserDataNames.pub_imposes_compare_id, cid); i++; } } @@ -6032,7 +6174,7 @@ private void executeTransforms() throws FHIRException, Exception { for (StructureMap map : worklist) { StructureMapAnalysis analysis = utils.analyse(null, map); - map.setUserData("analysis", analysis); + map.setUserData(UserDataNames.pub_analysis, analysis); for (StructureDefinition sd : analysis.getProfiles()) { FetchedResource nr = new FetchedResource(f.getName()+" (ex transform)"); nr.setElement(convertToElement(nr, sd)); @@ -6169,14 +6311,14 @@ private void loadAsBinaryResource(FetchedFile file, FetchedResource r, Implement r.setElement(e).setId(bin.getId()); r.setResource(bin); r.setResEntry(srcForLoad); - srcForLoad.setUserData("loaded.resource", r); + srcForLoad.setUserData(UserDataNames.pub_loaded_resource, r); r.setResEntry(srcForLoad); if (srcForLoad.hasProfile()) { - r.getElement().setUserData("logical", srcForLoad.getProfile().get(0).getValue()); + r.getElement().setUserData(UserDataNames.pub_logical, srcForLoad.getProfile().get(0).getValue()); r.setExampleUri(srcForLoad.getProfile().get(0).getValue()); } igpkp.findConfiguration(file, r); - srcForLoad.setUserData("loaded.resource", r); + srcForLoad.setUserData(UserDataNames.pub_loaded_resource, r); } private void loadAsElementModel(FetchedFile file, FetchedResource r, ImplementationGuideDefinitionResourceComponent srcForLoad, boolean suppressLoading, String cause) throws Exception { @@ -6280,10 +6422,10 @@ else if (file.getContentType().contains("xml")) r.setElement(e); } if (srcForLoad != null) { - srcForLoad.setUserData("loaded.resource", r); + srcForLoad.setUserData(UserDataNames.pub_loaded_resource, r); r.setResEntry(srcForLoad); if (srcForLoad.hasProfile()) { - r.getElement().setUserData("profile", srcForLoad.getProfile().get(0).getValue()); + r.getElement().setUserData(UserDataNames.map_profile, srcForLoad.getProfile().get(0).getValue()); r.getStatedProfiles().add(stripVersion(srcForLoad.getProfile().get(0).getValue())); } } @@ -6317,7 +6459,7 @@ else if (file.getContentType().contains("xml")) altered = true; } } - if (!binary && ((altered && r.getResource() != null) || (ver.equals(Constants.VERSION) && r.getResource() == null && context.getResourceNamesAsSet().contains(r.fhirType())))) { + if (!binary && !customResourceNames.contains(r.fhirType()) && ((altered && r.getResource() != null) || (ver.equals(Constants.VERSION) && r.getResource() == null && context.getResourceNamesAsSet().contains(r.fhirType())))) { r.setResource(new ObjectConverter(context).convert(r.getElement())); if (!r.getResource().hasId() && r.getId() != null) { r.getResource().setId(r.getId()); @@ -6589,7 +6731,7 @@ private void load(String type, boolean isMandatory) throws Exception { } else { r.setResource(parse(f)); } - r.getResource().setUserData("element", r.getElement()); + r.getResource().setUserData(UserDataNames.pub_element, r.getElement()); } catch (Exception e) { if (isMandatory) { throw new FHIRException("Error parsing "+f.getName()+": "+e.getMessage(), e); @@ -6986,11 +7128,11 @@ private void generateSnapshot(FetchedFile f, FetchedResource r, StructureDefinit if (base == null) { throw new Exception("Cannot find or generate snapshot for base definition ("+sd.getBaseDefinition()+" from "+sd.getUrl()+")"); } - if (sd.hasUserData("profileutils.snapshot.generated")) { + if (sd.hasUserData(UserDataNames.SNAPSHOT_GENERATED)) { changed = true; // we already tried to generate the snapshot, and maybe there were messages? if there are, // put them in the right place - List vmsgs = (List) sd.getUserData("profileutils.snapshot.generated.messages"); + List vmsgs = (List) sd.getUserData(UserDataNames.SNAPSHOT_GENERATED_MESSAGES); if (vmsgs != null && !vmsgs.isEmpty()) { f.getErrors().addAll(vmsgs); } @@ -7022,8 +7164,8 @@ private void generateSnapshot(FetchedFile f, FetchedResource r, StructureDefinit } utils.setDefWebRoot(igpkp.getCanonical()); try { - if (base.getUserString("webroot") != null) { - utils.generateSnapshot(base, sd, sd.getUrl(), base.getUserString("webroot"), sd.getName()); + if (base.getUserString(UserDataNames.render_webroot) != null) { + utils.generateSnapshot(base, sd, sd.getUrl(), base.getUserString(UserDataNames.render_webroot), sd.getName()); } else { utils.generateSnapshot(base, sd, sd.getUrl(), null, sd.getName()); } @@ -7047,7 +7189,7 @@ private void generateSnapshot(FetchedFile f, FetchedResource r, StructureDefinit if (changed || (!r.getElement().hasChild("snapshot") && sd.hasSnapshot())) { r.setElement(convertToElement(r, sd)); } - r.getElement().setUserData("profileutils.snapshot.errors", messages); + r.getElement().setUserData(UserDataNames.SNAPSHOT_ERRORS, messages); f.getErrors().addAll(messages); r.setSnapshotted(true); logDebugMessage(LogCategory.CONTEXT, "Context.See "+sd.getUrl()); @@ -7083,10 +7225,10 @@ private void validateExpressions(FetchedFile f, StructureDefinition sd, FetchedR private void validateExpression(FetchedFile f, StructureDefinition sd, FHIRPathEngine fpe, ElementDefinition ed, ElementDefinitionConstraintComponent inv, FetchedResource r) { if (inv.hasExpression()) { try { - ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache"); + ExpressionNode n = (ExpressionNode) inv.getUserData(UserDataNames.validator_expression_cache); if (n == null) { n = fpe.parse(inv.getExpression(), sd.getUrl()+"#"+ed.getId()+" / "+inv.getKey()); - inv.setUserData("validator.expression.cache", n); + inv.setUserData(UserDataNames.validator_expression_cache, n); } fpe.check(null, sd, ed.getPath(), n); } catch (Exception e) { @@ -7356,7 +7498,7 @@ private void validate(FetchedFile f, FetchedResource r, List private void validate(FetchedFile f, FetchedResource r, List errs, Resource ber) { long ts = System.currentTimeMillis(); - validator.validate(r.getElement(), errs, ber, ber.getUserString("profile")); + validator.validate(r.getElement(), errs, ber, ber.getUserString(UserDataNames.map_profile)); long tf = System.currentTimeMillis(); if (tf-ts > validationLogTime && validationLogTime > 0) { reportLongValidation(f, r, tf-ts); @@ -7606,8 +7748,8 @@ private void validate(FetchedFile file, FetchedResource r) throws Exception { Session tts = tt.start("validation"); List errs = new ArrayList(); - r.getElement().setUserData("igpub.context.file", file); - r.getElement().setUserData("igpub.context.resource", r); + r.getElement().setUserData(UserDataNames.pub_context_file, file); + r.getElement().setUserData(UserDataNames.pub_context_resource, r); validator.setExample(r.isExample()); if (r.isValidateAsResource()) { Resource res = r.getResource(); @@ -7616,11 +7758,11 @@ private void validate(FetchedFile file, FetchedResource r) throws Exception { for (BundleEntryComponent be : ((Bundle) res).getEntry()) { Resource ber = be.getResource(); - if (ber.hasUserData("profile")) { + if (ber.hasUserData(UserDataNames.map_profile)) { validate(file, r, errs, ber); } } - } else if (res.hasUserData("profile")) { + } else if (res.hasUserData(UserDataNames.map_profile)) { validate(file, r, errs, res); } } else if (r.getResource() != null && r.getResource() instanceof Binary && file.getLogical() != null && context.hasResource(StructureDefinition.class, file.getLogical())) { @@ -7634,8 +7776,8 @@ private void validate(FetchedFile file, FetchedResource r) throws Exception { validator.setNoCheckAggregation(r.isExample() && ToolingExtensions.readBoolExtension(r.getResEntry(), "http://hl7.org/fhir/tools/StructureDefinition/igpublisher-no-check-aggregation")); List profiles = new ArrayList<>(); - if (r.getElement().hasUserData("profile")) { - addProfile(profiles, r.getElement().getUserString("profile"), null); + if (r.getElement().hasUserData(UserDataNames.map_profile)) { + addProfile(profiles, r.getElement().getUserString(UserDataNames.map_profile), null); } for (String s : r.getProfiles(false)) { addProfile(profiles, s, r.fhirType()); @@ -8176,7 +8318,7 @@ private void regenerate(String uri) throws Exception { r.setValidated(true); r.setElement(convertToElement(r, bc)); igpkp.findConfiguration(f, r); - bc.setUserData("config", r.getConfig()); + bc.setUserData(UserDataNames.pub_resource_config, r.getConfig()); generateNativeOutputs(f, true, null); generateHtmlOutputs(f, true, null); } @@ -8711,6 +8853,10 @@ private boolean runJekyll() throws IOException, InterruptedException { vars.putAll(env); String path = FhirSettings.getRubyPath()+":"+env.get("PATH"); vars.put("PATH", path); + if (FhirSettings.getGemPath() != null) { + vars.put("GEM_PATH", FhirSettings.getGemPath()); + } + System.out.println("r:"+vars.get("RUBY_ROOT")); CommandLine commandLine = new CommandLine("bash").addArgument("-c").addArgument(jekyllCommand+" build --destination "+outputDir, false); exec.execute(commandLine, vars); } else { @@ -9236,7 +9382,7 @@ private void saveCSList(String name, List cslist, DBBuilder db, int oidArr.add(s); } } - Set rl = (Set) cs.getUserData("xref.used"); + Set rl = (Set) cs.getUserData(UserDataNames.pub_xref_used); Set links = new HashSet<>(); if (rl != null) { JsonObject uses = new JsonObject(); @@ -9319,7 +9465,7 @@ private void saveVSList(String name, List vslist, DBBuilder db, int vi } } - Set sources = (Set) vs.getUserData("xref.sources"); + Set sources = (Set) vs.getUserData(UserDataNames.pub_xref_sources); if (!oids.isEmpty()) { JsonArray srcArr = new JsonArray(); item.add("sources", srcArr); @@ -9328,7 +9474,7 @@ private void saveVSList(String name, List vslist, DBBuilder db, int vi } } - Set rl = (Set) vs.getUserData("xref.used"); + Set rl = (Set) vs.getUserData(UserDataNames.pub_xref_used); Set links = new HashSet<>(); if (rl != null) { JsonObject uses = new JsonObject(); @@ -10814,7 +10960,7 @@ private void generateHtmlOutputs(FetchedFile f, boolean regen, DBBuilder db) thr generateResourceFragments(f, r, System.currentTimeMillis()); }*/ List clist = new ArrayList<>(); - if (r.getResource() == null) { + if (r.getResource() == null && !r.isCustomResource()) { try { Resource container = convertFromElement(r.getElement()); r.setResource(container); @@ -11233,6 +11379,14 @@ private String processRefTag(DBBuilder db, String src, FetchedFile f) { private ContextUtilities cu; private boolean logLoading; + + private JsonObject approvedIgsForCustomResources; + private Set customResourceNames = new HashSet<>(); + private List customResources = new ArrayList<>(); + + private boolean needsRegen = false; + + private ValidatorSession validatorSession; private String processSQLCommand(DBBuilder db, String src, FetchedFile f) throws FHIRException, IOException { long start = System.currentTimeMillis(); @@ -11784,8 +11938,25 @@ private byte[] saveNativeResourceOutputs(FetchedFile f, FetchedResource r) throw Element element = r.getElement(); Element eNN = element; jp.compose(element, bsj, OutputStyle.NORMAL, igpkp.getCanonical()); - npm.addFile(isExample(f,r ) ? Category.EXAMPLE : Category.RESOURCE, element.fhirType()+"-"+r.getId()+".json", bsj.toByteArray()); - + if (!r.isCustomResource()) { + npm.addFile(isExample(f,r ) ? Category.EXAMPLE : Category.RESOURCE, element.fhirType()+"-"+r.getId()+".json", bsj.toByteArray()); + } else if ("StructureDefinition".equals(r.fhirType())) { + npm.addFile(Category.RESOURCE, element.fhirType()+"-"+r.getId()+".json", bsj.toByteArray()); + StructureDefinition sdt = (StructureDefinition) r.getResource().copy(); + sdt.setKind(StructureDefinitionKind.RESOURCE); + bsj = new ByteArrayOutputStream(); + new JsonParser().setOutputStyle(OutputStyle.NORMAL).compose(bsj, sdt); + npm.addFile(Category.CUSTOM, "StructureDefinition-"+r.getId()+".json", bsj.toByteArray()); + } else { + npm.addFile(Category.CUSTOM, element.fhirType()+"-"+r.getId()+".json", bsj.toByteArray()); + Binary bin = new Binary("application/fhir+json"); + bin.setId(r.getId()); + bin.setContent(bsj.toByteArray()); + bsj = new ByteArrayOutputStream(); + new JsonParser().setOutputStyle(OutputStyle.NORMAL).compose(bsj, bin); + npm.addFile(isExample(f,r ) ? Category.EXAMPLE : Category.RESOURCE, "Binary-"+r.getId()+".json", bsj.toByteArray()); + } + if (module.isNoNarrative()) { // we don't use the narrative in these resources in _includes, so we strip it - it slows Jekyll down greatly eNN = (Element) element.copy(); @@ -12123,12 +12294,13 @@ private void generateHtml(FetchedFile f, FetchedResource r, Map } else { if (xhtml == null) { RenderingContext xlrc = lrc.copy(false); + ResourceRenderer rr = RendererFactory.factory(r.fhirType(), xlrc); if (r.getResource() != null && r.getResource() instanceof DomainResource) { - xhtml = RendererFactory.factory(r.fhirType(), xlrc).buildNarrative(ResourceWrapper.forResource(xlrc, r.getResource())); + xhtml = rr.buildNarrative(ResourceWrapper.forResource(xlrc, r.getResource())); } else { ResourceWrapper rw = ResourceWrapper.forResource(xlrc, r.getElement()); try { - xhtml = RendererFactory.factory(r.fhirType(), xlrc).buildNarrative(rw); + xhtml = rr.buildNarrative(rw); } catch (Exception ex) { xhtml = new XhtmlNode(NodeType.Element, "div"); xhtml.para("Error rendering resource: "+ex.getMessage()); @@ -12823,7 +12995,7 @@ private void generateOutputsStructureDefinition(FetchedFile f, FetchedResource r StructureDefinition sdi = context.fetchResource(StructureDefinition.class, ext.getValue().primitiveValue()); if (sdi != null) { start = System.currentTimeMillis(); - String cid = sdi.getUserString("imposes.compare.id"); + String cid = sdi.getUserString(UserDataNames.pub_imposes_compare_id); fragment("StructureDefinition-imposes-"+prefixForContainer+sd.getId()+"-"+cid+langSfx, sdr.compareImposes(sdi), f.getOutputNames(), r, vars, null, start, "imposes", "StructureDefinition"); } } @@ -13068,7 +13240,7 @@ private String getTitle(FetchedFile f, FetchedResource r) { return t; } for (ImplementationGuideDefinitionResourceComponent res : publishedIg.getDefinition().getResource()) { - FetchedResource tr = (FetchedResource) res.getUserData("loaded.resource"); + FetchedResource tr = (FetchedResource) res.getUserData(UserDataNames.pub_loaded_resource); if (tr == r) { return res.getDescription(); } @@ -14149,9 +14321,9 @@ public void recordProfileUsage(StructureDefinition profile, Object appContext, E if (profile != null && profile.getUrl().startsWith(igpkp.getCanonical())) { // ignore anything we didn't define FetchedResource example; if (appContext instanceof ValidationContext) { - example = (FetchedResource) ((ValidationContext) appContext).getResource().getUserData("igpub.context.resource"); + example = (FetchedResource) ((ValidationContext) appContext).getResource().getUserData(UserDataNames.pub_context_resource); } else { - example= (FetchedResource) ((Element) appContext).getUserData("igpub.context.resource"); + example= (FetchedResource) ((Element) appContext).getUserData(UserDataNames.pub_context_resource); } if (example != null) { FetchedResource source = null; diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DBBuilder.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DBBuilder.java index fafe91fd6..803c97680 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DBBuilder.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/DBBuilder.java @@ -37,6 +37,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; import org.hl7.fhir.utilities.Utilities; @@ -321,47 +322,54 @@ public void saveResource(FetchedFile f, FetchedResource r, byte[] json) { try { if (r.getResource() == null || !(r.getResource() instanceof CanonicalResource)) { - PreparedStatement psql = con.prepareStatement("Insert into Resources (key, type, id, web, name, description, json) values (?, ?, ?, ?, ?, ?, ?)"); + PreparedStatement psql = con.prepareStatement("Insert into Resources (key, type, custom, id, web, url, version, status, date, name, title, description, json) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); psql.setInt(1, ++lastResKey); bindString(psql, 2, r.fhirType()); - bindString(psql, 3, r.getId()); - bindString(psql, 4, r.getElement().getWebPath()); - bindString(psql, 5, r.getResourceName()); - bindString(psql, 6, r.getResourceDescription()); - psql.setBytes(7, json); + psql.setInt(3, r.getElement().getProperty().getStructure().hasUserData(UserDataNames.loader_custom_resource) ? 1 : 0); + bindString(psql, 4, r.getId()); + bindString(psql, 5, r.getElement().getWebPath()); + bindString(psql, 6, r.getElement().getNamedChildValue("url")); + bindString(psql, 7, r.getElement().getNamedChildValue("version")); + bindString(psql, 8, r.getElement().getNamedChildValue("status")); + bindString(psql, 9, r.getElement().getNamedChildValue("date")); + bindString(psql, 10, r.getElement().hasChild("name") && r.getElement().getNamedChild("name").isPrimitive() ? r.getElement().getNamedChildValue("name") : r.getResourceName()); + bindString(psql, 11, r.getElement().getNamedChildValue("title")); + bindString(psql, 12, r.getResourceDescription()); + psql.setBytes(13, json); psql.executeUpdate(); - r.getElement().setUserData("db.key", lastResKey); - r.getElement().setUserData("Storage.key", lastResKey); + r.getElement().setUserData(UserDataNames.db_key, lastResKey); + r.getElement().setUserData(UserDataNames.Storage_key, lastResKey); } else { CanonicalResource cr = (CanonicalResource) r.getResource(); - PreparedStatement psql = con.prepareStatement("Insert into Resources (key, type, id, web, url, version, status, date, name, title, experimental, realm, description, purpose, copyright, copyrightLabel, json) "+ - "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + PreparedStatement psql = con.prepareStatement("Insert into Resources (key, type, custom, id, web, url, version, status, date, name, title, experimental, realm, description, purpose, copyright, copyrightLabel, json) "+ + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); psql.setInt(1, ++lastResKey); bindString(psql, 2, r.fhirType()); - bindString(psql, 3, r.getId()); - bindString(psql, 4, cr.getWebPath()); - bindString(psql, 5, cr.getUrl()); - bindString(psql, 6, cr.getVersion()); - bindString(psql, 7, cr.getStatus().toCode()); - bindString(psql, 8, cr.getDateElement().primitiveValue()); - bindString(psql, 9, cr.getName()); - bindString(psql, 10, cr.getTitle()); - bindString(psql, 11, cr.getExperimentalElement().primitiveValue()); - bindString(psql, 12, realm(cr)); - bindString(psql, 13, cr.getDescription()); - bindString(psql, 14, cr.getPurpose()); - bindString(psql, 15, cr.getCopyright()); - bindString(psql, 16, cr.getCopyrightLabel()); - psql.setBytes(17, json); + psql.setInt(3, r.getElement().getProperty().getStructure().hasUserData(UserDataNames.loader_custom_resource) ? 1 : 0); + bindString(psql, 4, r.getId()); + bindString(psql, 5, cr.getWebPath()); + bindString(psql, 6, cr.getUrl()); + bindString(psql, 7, cr.getVersion()); + bindString(psql, 8, cr.getStatus().toCode()); + bindString(psql, 9, cr.getDateElement().primitiveValue()); + bindString(psql, 10, cr.getName()); + bindString(psql, 11, cr.getTitle()); + bindString(psql, 12, cr.getExperimentalElement().primitiveValue()); + bindString(psql, 13, realm(cr)); + bindString(psql, 14, cr.getDescription()); + bindString(psql, 15, cr.getPurpose()); + bindString(psql, 16, cr.getCopyright()); + bindString(psql, 17, cr.getCopyrightLabel()); + psql.setBytes(18, json); psql.executeUpdate(); if (cr instanceof CodeSystem) { codesystems.add((CodeSystem) cr); } else if (cr instanceof ConceptMap) { mappings.add((ConceptMap) cr); } - cr.setUserData("db.key", lastResKey); - cr.setUserData("Storage.key", lastResKey); - r.getElement().setUserData("Storage.key", lastResKey); + cr.setUserData(UserDataNames.db_key, lastResKey); + cr.setUserData(UserDataNames.Storage_key, lastResKey); + r.getElement().setUserData(UserDataNames.Storage_key, lastResKey); } } catch (SQLException e) { errors.add(e.getMessage()); @@ -384,13 +392,13 @@ public void finishResources() { for (CodeSystem cs : codesystems) { for (PropertyComponent p : cs.getProperty()) { psql.setInt(1, ++lastPropKey); - psql.setInt(2, ((Integer) cs.getUserData("db.key")).intValue()); + psql.setInt(2, ((Integer) cs.getUserData(UserDataNames.db_key)).intValue()); bindString(psql, 3, p.getCode()); bindString(psql, 4, p.getUri()); bindString(psql, 5, p.getDescription()); bindString(psql, 6, p.getType().toCode()); psql.executeUpdate(); - p.setUserData("db.key", lastPropKey); + p.setUserData(UserDataNames.db_key, lastPropKey); } } psql = con.prepareStatement("Insert into Concepts (Key, ResourceKey, ParentKey, Code, Display, Definition) "+ @@ -416,7 +424,7 @@ public void finishResources() { for (SourceElementComponent src : grp.getElement()) { for (TargetElementComponent tgt : src.getTarget()) { psql.setInt(1, ++lastMapKey); - psql.setInt(2, ((Integer) cm.getUserData("db.key")).intValue()); + psql.setInt(2, ((Integer) cm.getUserData(UserDataNames.db_key)).intValue()); bindString(psql, 3, grp.getSourceElement().baseUrl()); bindString(psql, 4, grp.getSourceElement().version()); bindString(psql, 5, src.getCode()); @@ -464,9 +472,9 @@ public void recordExpansion(ValueSet vs, ValueSetExpansionOutcome exp) throws SQ private void addContains(ValueSet vs, ValueSetExpansionContainsComponent e, PreparedStatement psql) throws SQLException { - if (vs.hasUserData("db.key")) { + if (vs.hasUserData(UserDataNames.db_key)) { psql.setInt(1, ++lastVSKey); - psql.setInt(2, ((Integer) vs.getUserData("db.key")).intValue()); + psql.setInt(2, ((Integer) vs.getUserData(UserDataNames.db_key)).intValue()); bindString(psql, 3, vs.getUrl()); bindString(psql, 4, vs.getVersion()); bindString(psql, 5, e.getSystem()); @@ -481,10 +489,10 @@ private void addContains(ValueSet vs, ValueSetExpansionContainsComponent e, Prep } private void addConcepts(CodeSystem cs, List list, PreparedStatement psql, int parent) throws SQLException { - if (cs.hasUserData("db.key")) { + if (cs.hasUserData(UserDataNames.db_key)) { for (ConceptDefinitionComponent cd : list) { psql.setInt(1, ++lastConceptKey); - psql.setInt(2, ((Integer) cs.getUserData("db.key")).intValue()); + psql.setInt(2, ((Integer) cs.getUserData(UserDataNames.db_key)).intValue()); if (parent == 0) { psql.setNull(3, java.sql.Types.INTEGER); } else { @@ -494,29 +502,29 @@ private void addConcepts(CodeSystem cs, List list, P bindString(psql, 5, cd.getDisplay()); bindString(psql, 6, cd.getDefinition()); psql.executeUpdate(); - cd.setUserData("db.key", lastConceptKey); + cd.setUserData(UserDataNames.db_key, lastConceptKey); addConcepts(cs, cd.getConcept(), psql, lastConceptKey); } } } private void addConceptProperties(CodeSystem cs, List list, PreparedStatement psql) throws SQLException { - if (cs.hasUserData("db.key")) { + if (cs.hasUserData(UserDataNames.db_key)) { for (ConceptDefinitionComponent cd : list) { for (ConceptPropertyComponent p : cd.getProperty()) { psql.setInt(1, ++lastCPropKey); - psql.setInt(2, ((Integer) cs.getUserData("db.key")).intValue()); - psql.setInt(3, ((Integer) cd.getUserData("db.key")).intValue()); + psql.setInt(2, ((Integer) cs.getUserData(UserDataNames.db_key)).intValue()); + psql.setInt(3, ((Integer) cd.getUserData(UserDataNames.db_key)).intValue()); PropertyComponent pd = getPropDefn(p.getCode(), cs); if (pd == null) { psql.setNull(4, java.sql.Types.INTEGER); } else { - psql.setInt(4, ((Integer) pd.getUserData("db.key")).intValue()); + psql.setInt(4, ((Integer) pd.getUserData(UserDataNames.db_key)).intValue()); } bindString(psql, 5, p.getCode()); bindString(psql, 6, p.getValue() == null ? p.getValue().primitiveValue() : null); psql.executeUpdate(); - p.setUserData("db.key", lastCPropKey); + p.setUserData(UserDataNames.db_key, lastCPropKey); } addConceptProperties(cs, cd.getConcept(), psql); } @@ -524,18 +532,18 @@ private void addConceptProperties(CodeSystem cs, List list, PreparedStatement psql) throws SQLException { - if (cs.hasUserData("db.key")) { + if (cs.hasUserData(UserDataNames.db_key)) { for (ConceptDefinitionComponent cd : list) { for (ConceptDefinitionDesignationComponent p : cd.getDesignation()) { psql.setInt(1, ++lastDesgKey); - psql.setInt(2, ((Integer) cs.getUserData("db.key")).intValue()); - psql.setInt(3, ((Integer) cd.getUserData("db.key")).intValue()); + psql.setInt(2, ((Integer) cs.getUserData(UserDataNames.db_key)).intValue()); + psql.setInt(3, ((Integer) cd.getUserData(UserDataNames.db_key)).intValue()); bindString(psql, 4, p.getUse().getSystem()); bindString(psql, 5, p.getUse().getCode()); bindString(psql, 6, p.getLanguage()); bindString(psql, 7, p.getValue()); psql.executeUpdate(); - p.setUserData("db.key", lastDesgKey); + p.setUserData(UserDataNames.db_key, lastDesgKey); } addConceptDesignations(cs, cd.getConcept(), psql); } @@ -673,6 +681,7 @@ private void makeResourcesTable(Connection con) throws SQLException { stmt.execute("CREATE TABLE Resources (\r\n"+ "Key integer NOT NULL,\r\n"+ "Type nvarchar NOT NULL,\r\n"+ + "Custom integer NOT NULL,\r\n"+ "Id nvarchar NOT NULL,\r\n"+ "Web nvarchar NOT NULL,\r\n"+ "Url nvarchar NULL,\r\n"+ @@ -907,7 +916,7 @@ public String processSQL(String sql) { FetchedResource tgt = null; for (FetchedFile f : files) { for (FetchedResource rf : f.getResources()) { - String key = rf.getElement().getUserString("Storage.key"); + String key = rf.getElement().getUserString(UserDataNames.Storage_key); if (s.equals(key)) { tgt = rf; } @@ -1007,8 +1016,8 @@ public void addToCSList(int viewType, CodeSystem cs, Set oids, Set oids, Set oids, Set sql = con.prepareStatement("insert into ValueSetList (ValueSetListKey, ViewType, ResourceKey, Url, Version, Status, Name, Title, Description) values (?, ?, ?, ?, ?, ?, ?, ?, ?)"); sql.setInt(1, lastVLKey); sql.setInt(2, viewType); - if (vs.hasUserData("db.key")) { - sql.setInt(3, (int) vs.getUserData("db.key")); + if (vs.hasUserData(UserDataNames.db_key)) { + sql.setInt(3, (int) vs.getUserData(UserDataNames.db_key)); } else { sql.setNull(3, java.sql.Types.INTEGER); } @@ -1101,8 +1110,8 @@ public void addToVSList(int viewType, ValueSet vs, Set oids, Set sql.setInt(1, lastVLKey); sql.setString(2, r.fhirType()); sql.setString(3, r.getIdBase()); - if (vs.hasUserData("db.key")) { - sql.setInt(4, (int) vs.getUserData("db.key")); + if (vs.hasUserData(UserDataNames.db_key)) { + sql.setInt(4, (int) vs.getUserData(UserDataNames.db_key)); } else { sql.setNull(4, java.sql.Types.INTEGER); } diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java index 6a25f438a..2d98ba923 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/renderers/StructureDefinitionRenderer.java @@ -67,6 +67,7 @@ import org.hl7.fhir.r5.terminologies.ValueSetUtilities; import org.hl7.fhir.r5.utils.ElementDefinitionUtilities; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.r5.utils.UserDataNames; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.StandardsStatus; @@ -282,7 +283,7 @@ private String extensionSummary() { if (ed.getPath().endsWith(".extension") && ed.hasSliceName()) { slice = ed; } else if (ed.getPath().endsWith(".extension.value[x]")) { - ed.setUserData("slice", slice); + ed.setUserData(UserDataNames.render_extension_slice, slice); subs.add(ed); slice = null; } @@ -291,7 +292,7 @@ private String extensionSummary() { String html = Utilities.stripAllPara(processMarkdown("description", sd.getDescriptionElement())); b.append("

    Complex Extension: "+html+"

      "); for (ElementDefinition ed : subs) { - ElementDefinition defn = (ElementDefinition) ed.getUserData("slice"); + ElementDefinition defn = (ElementDefinition) ed.getUserData(UserDataNames.render_extension_slice); if (defn != null) { b.append("
    • "+(defn.getSliceName())+": "+ed.typeSummary()+": "+Utilities.stripPara(processMarkdown("ext-desc", defn.getDefinition()))+"
    • \r\n"); } @@ -305,7 +306,7 @@ private boolean parentChainHasOptional(ElementDefinition ed, StructureDefinition if (!ed.getPath().contains(".")) return false; - ElementDefinition match = (ElementDefinition) ed.getUserData(ProfileUtilities.UD_DERIVATION_POINTER); + ElementDefinition match = (ElementDefinition) ed.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER); if (match == null) return true; // really, we shouldn't get here, but this appears to be common in the existing profiles? // throw new Error("no matches for "+ed.getPath()+"/"+ed.getName()+" in "+profile.getUrl()); @@ -532,7 +533,7 @@ protected List getMustSupportElements() { edCopy.getExample().clear(); if (!edCopy.getMustSupport()) { if (edCopy.getPath().contains(".")) { - edCopy.setUserData("render.opaque", true); + edCopy.setUserData(UserDataNames.render_opaque, true); } edCopy.setBinding(null); edCopy.getConstraint().clear(); @@ -674,15 +675,15 @@ public String txDiff(boolean withHeadings, boolean mustSupportOnly) throws FHIRE String id = ed.getId(); if (ed.hasFixed()) { hasFixed = true; - ed.getBinding().setUserData("tx.value", ed.getFixed()); + ed.getBinding().setUserData(UserDataNames.render_tx_value, ed.getFixed()); } else if (ed.hasPattern()) { hasFixed = true; - ed.getBinding().setUserData("tx.pattern", ed.getPattern()); + ed.getBinding().setUserData(UserDataNames.render_tx_pattern, ed.getPattern()); } else { // tricky : scan the children for a fixed coding value DataType t = findFixedValue(ed, true); if (t != null) - ed.getBinding().setUserData("tx.value", t); + ed.getBinding().setUserData(UserDataNames.render_tx_value, t); } if (ed.getType().size() == 1 && ed.getType().get(0).getWorkingCode().equals("Extension")) id = id + "
      " + ed.getType().get(0).getProfile(); @@ -715,15 +716,15 @@ public String tx(boolean withHeadings, boolean mustSupportOnly, boolean keyOnly) String id = ed.getId(); if (ed.hasFixed()) { hasFixed = true; - ed.getBinding().setUserData("tx.value", ed.getFixed()); + ed.getBinding().setUserData(UserDataNames.render_tx_value, ed.getFixed()); } else if (ed.hasPattern()) { hasFixed = true; - ed.getBinding().setUserData("tx.pattern", ed.getPattern()); + ed.getBinding().setUserData(UserDataNames.render_tx_pattern, ed.getPattern()); } else { // tricky : scan the children for a fixed coding value DataType t = findFixedValue(ed, false); if (t != null) - ed.getBinding().setUserData("tx.value", t); + ed.getBinding().setUserData(UserDataNames.render_tx_value, t); } if (ed.getType().size() == 1 && ed.getType().get(0).getWorkingCode().equals("Extension")) id = id + "
      " + ed.getType().get(0).getProfile(); @@ -773,16 +774,16 @@ public void txItem(Map txmap, StringBuilder b, String String link = null; if (tx.hasValueSet()) { link = txDetails(tx, brd, false); - } else if (ed.hasUserData(ProfileUtilities.UD_DERIVATION_POINTER)) { - ElementDefinitionBindingComponent txi = ((ElementDefinition) ed.getUserData(ProfileUtilities.UD_DERIVATION_POINTER)).getBinding(); + } else if (ed.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)) { + ElementDefinitionBindingComponent txi = ((ElementDefinition) ed.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)).getBinding(); link = txDetails(txi, brd, true); } boolean strengthInh = false; BindingStrength strength = null; if (tx.hasStrength()) { strength = tx.getStrength(); - } else if (ed.hasUserData(ProfileUtilities.UD_DERIVATION_POINTER)) { - ElementDefinitionBindingComponent txi = ((ElementDefinition) ed.getUserData(ProfileUtilities.UD_DERIVATION_POINTER)).getBinding(); + } else if (ed.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)) { + ElementDefinitionBindingComponent txi = ((ElementDefinition) ed.getUserData(UserDataNames.SNAPSHOT_DERIVATION_POINTER)).getBinding(); strength = txi.getStrength(); strengthInh = true; } @@ -793,11 +794,11 @@ public void txItem(Map txmap, StringBuilder b, String else if (!tx.hasDescription()) System.out.println("No value set specified at " + url + "#" + path + " (no url)"); } - if (tx.hasUserData("tx.value")) { - brd.vss = "Fixed Value: " + summariseValue((DataType) tx.getUserData("tx.value")); + if (tx.hasUserData(UserDataNames.render_tx_value)) { + brd.vss = "Fixed Value: " + summariseValue((DataType) tx.getUserData(UserDataNames.render_tx_value)); brd.suffix = null; - } else if (tx.hasUserData("tx.pattern")) { - brd.vss = "Pattern: " + summariseValue((DataType) tx.getUserData("tx.pattern")); + } else if (tx.hasUserData(UserDataNames.render_tx_pattern)) { + brd.vss = "Pattern: " + summariseValue((DataType) tx.getUserData(UserDataNames.render_tx_pattern)); brd.suffix = null; } @@ -863,8 +864,8 @@ else if (Utilities.isAbsoluteUrlLinkable(br.url)) } } else { String p = vs.getWebPath(); - if (vs.hasUserData("External.Link")) { - link = vs.getUserString("External.Link"); + if (vs.hasUserData(UserDataNames.render_external_link)) { + link = vs.getUserString(UserDataNames.render_external_link); } else if (vs.hasSourcePackage()) { if (VersionUtilities.isCorePackage(vs.getSourcePackage().getId())) { link = "the FHIR Standard"; @@ -881,7 +882,7 @@ else if (p.startsWith("http:")) b.append("" + Utilities.escapeXml(gen.getTranslated(vs.getNameElement()))); else b.append("" + Utilities.escapeXml(gen.getTranslated(vs.getNameElement()))); - if (vs.hasUserData("External.Link")) { + if (vs.hasUserData(UserDataNames.render_external_link)) { b.append(" \".\"/"); } b.append(""); @@ -1710,7 +1711,7 @@ else if (hasResource(t)) if (elem.hasBinding() && elem.getBinding().hasValueSet()) { ValueSet vs = context.findTxResource(ValueSet.class, elem.getBinding().getValueSet()); if (vs != null) - b.append(" " + Utilities.escapeXml(elem.getShort()) + ""); + b.append(" " + Utilities.escapeXml(elem.getShort()) + ""); else b.append(" " + Utilities.escapeXml(elem.getShort()) + ""); } else diff --git a/pom.xml b/pom.xml index bc647e225..3c0cabf44 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ pom - 6.4.1 + 6.4.2-SNAPSHOT 3.0.0-M5 5.2.1 4.11.0 From b89e5b10f0603340f12e756cdb4309437716fd9a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 18 Nov 2024 20:16:44 +1100 Subject: [PATCH 3/3] custom resource workaround for sushi --- .../hl7/fhir/igtools/publisher/Publisher.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java index 546d50b36..0711b6e0c 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/Publisher.java @@ -5041,6 +5041,25 @@ private String loadCustomResource(String filename) throws FileNotFoundException, def.setUserData(UserDataNames.loader_custom_resource, "true"); def.setWebPath("placeholder.html"); // we'll figure it out later context.cacheResource(def); + + // work around for a sushi limitation + for (ImplementationGuideDefinitionResourceComponent res : publishedIg.getDefinition().getResource()) { + if (res.getReference().getReference().startsWith("Binary/")) { + if (res.getProfile().size() == 1 && def.getUrl().equals(res.getProfile().get(0).primitiveValue())) { + String id = res.getReference().getReference().substring(res.getReference().getReference().indexOf("/")+1); + File of = new File(Utilities.path(Utilities.getDirectoryForFile(this.getConfigFile()), "fsh-generated", "resources", "Binary-"+id+".json")); + File nf = new File(Utilities.path(Utilities.getDirectoryForFile(this.getConfigFile()), "fsh-generated", "resources", def.getType()+"-"+id+".json")); + if (of.exists()) { + of.renameTo(nf); + JsonObject j = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(nf); + j.set("resourceType", def.getType()); + org.hl7.fhir.utilities.json.parser.JsonParser.compose(j, nf, true); + } + res.getReference().setReference(def.getType()+res.getReference().getReference().substring(res.getReference().getReference().indexOf("/"))); + } + } + } + return "loaded"; }