From 7ca698f36a7c47c38b168c1e03ea6190d9104ae3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 4 Sep 2020 23:49:36 +1000 Subject: [PATCH 1/2] fixes for 5.1.8 (#336) * Fix up conversion for date problems * Mark packages as unsuitable for publication * rework rendering tests & fix bug in Parameters renderer * fix test case depedency * update VSACImporter to get value sets from VSAC directly * Fix up rendering of Extensions * Add support for choice groups, and markdownify some elements --- .../convertors/VersionConvertor_10_40.java | 7 + .../conv10_40/MedicationRequest10_40.java | 6 +- .../fhir/convertors/misc/VSACImporter.java | 149 ++++++----- .../fhir/r5/conformance/ProfileUtilities.java | 246 ++++++++++++++++-- .../main/java/org/hl7/fhir/r5/model/Base.java | 3 +- .../hl7/fhir/r5/renderers/DataRenderer.java | 102 ++++++++ .../fhir/r5/renderers/ParametersRenderer.java | 10 +- .../r5/renderers/ProfileDrivenRenderer.java | 154 +++++++---- .../fhir/r5/renderers/RendererFactory.java | 3 + .../fhir/r5/renderers/utils/BaseWrappers.java | 8 +- .../fhir/r5/renderers/utils/DOMWrappers.java | 5 + .../r5/renderers/utils/DirectWrappers.java | 15 ++ .../r5/renderers/utils/ElementWrappers.java | 7 +- .../org/hl7/fhir/r5/utils/FHIRPathEngine.java | 14 + .../fhir/r5/utils/NPMPackageGenerator.java | 49 ++-- .../r5/test/NarrativeGenerationTests.java | 60 ++++- .../fhir/r5/test/ResourceRoundTripTests.java | 3 + .../hl7/fhir/utilities/cache/NpmPackage.java | 4 + .../hl7/fhir/utilities/xhtml/XhtmlNode.java | 6 + pom.xml | 2 +- 20 files changed, 675 insertions(+), 178 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_10_40.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_10_40.java index 431207402..c9550402d 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_10_40.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/VersionConvertor_10_40.java @@ -65,6 +65,7 @@ import org.hl7.fhir.convertors.conv10_40.ValueSet10_40; import org.hl7.fhir.dstu2.model.CodeableConcept; import org.hl7.fhir.dstu2.model.Parameters; import org.hl7.fhir.dstu2.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.dstu2.model.PositiveIntType; import org.hl7.fhir.dstu2.model.Reference; import org.hl7.fhir.dstu2.utils.ToolingExtensions; import org.hl7.fhir.exceptions.FHIRException; @@ -3423,4 +3424,10 @@ public class VersionConvertor_10_40 { public static org.hl7.fhir.dstu2.model.Resource convertResource(org.hl7.fhir.r4.model.Resource src) throws FHIRException { return convertResource(src, null); } + + public static UnsignedIntType convertUnsignedIntToPositive(PositiveIntType src) { + org.hl7.fhir.r4.model.UnsignedIntType tgt = src.hasValue() ? new org.hl7.fhir.r4.model.UnsignedIntType(src.getValue()) : new org.hl7.fhir.r4.model.UnsignedIntType(); + copyElement(src, tgt); + return tgt; + } } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/MedicationRequest10_40.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/MedicationRequest10_40.java index 25e745d33..e0e3bbed0 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/MedicationRequest10_40.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv10_40/MedicationRequest10_40.java @@ -15,7 +15,7 @@ public class MedicationRequest10_40 { tgt.setIntent(org.hl7.fhir.r4.model.MedicationRequest.MedicationRequestIntent.ORDER); for (org.hl7.fhir.dstu2.model.Identifier identifier : src.getIdentifier()) tgt.addIdentifier(VersionConvertor_10_40.convertIdentifier(identifier)); if (src.hasDateWritten()) - tgt.setAuthoredOn(src.getDateWritten()); + tgt.setAuthoredOnElement(VersionConvertor_10_40.convertDateTime(src.getDateWrittenElement())); if (src.hasStatus()) tgt.setStatus(org.hl7.fhir.r4.model.MedicationRequest.MedicationRequestStatus.fromCode(src.getStatus().toCode())); if (src.hasPatient()) @@ -48,7 +48,7 @@ public class MedicationRequest10_40 { return null; org.hl7.fhir.r4.model.Dosage tgt = new org.hl7.fhir.r4.model.Dosage(); if (src.hasText()) - tgt.setText(src.getText()); + tgt.setTextElement(VersionConvertor_10_40.convertString(src.getTextElement())); if (src.hasAdditionalInstructions()) tgt.addAdditionalInstruction(VersionConvertor_10_40.convertCodeableConcept(src.getAdditionalInstructions())); if (src.hasTiming()) @@ -79,7 +79,7 @@ public class MedicationRequest10_40 { if (src.hasValidityPeriod()) tgt.setValidityPeriod(VersionConvertor_10_40.convertPeriod(src.getValidityPeriod())); if (src.hasNumberOfRepeatsAllowed()) - tgt.setNumberOfRepeatsAllowed(src.getNumberOfRepeatsAllowed()); + tgt.setNumberOfRepeatsAllowedElement(VersionConvertor_10_40.convertUnsignedIntToPositive(src.getNumberOfRepeatsAllowedElement())); if (src.hasQuantity()) tgt.setQuantity(VersionConvertor_10_40.convertSimpleQuantity(src.getQuantity())); if (src.hasExpectedSupplyDuration()) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/VSACImporter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/VSACImporter.java index 2e03d4430..dd791b3db 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/VSACImporter.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/VSACImporter.java @@ -1,26 +1,29 @@ package org.hl7.fhir.convertors.misc; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r5.formats.JsonParser; -import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r4.utils.client.FHIRToolingClient; +import org.hl7.fhir.r4.formats.JsonParser; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.ValueSet; +import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.utilities.CSVReader; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.xml.XMLUtil; import org.w3c.dom.Element; import java.io.*; +import java.net.URISyntaxException; import java.text.ParseException; import java.util.ArrayList; import java.util.List; public class VSACImporter extends OIDBasedValueSetImporter { - public static void main(String[] args) throws FileNotFoundException, FHIRException, IOException, ParseException { + public static void main(String[] args) throws FileNotFoundException, FHIRException, IOException, ParseException, URISyntaxException { // new PhinVadsImporter().importValueSet(TextFile.fileToBytes("C:\\work\\org.hl7.fhir\\packages\\us.cdc.phinvads-source\\source\\PHVS_BirthDefectsLateralityatDiagnosis_HL7_V1.txt")); VSACImporter self = new VSACImporter(); - self.process(args[0], args[1]); + self.process(args[0], args[1], args[2], args[3]); } public VSACImporter() throws FileNotFoundException, FHIRException, IOException { @@ -28,69 +31,87 @@ public class VSACImporter extends OIDBasedValueSetImporter { init(); } - private void process(String source, String dest) { - for (File f : new File(source).listFiles()) { + private void process(String source, String dest, String username, String password) throws FHIRException, FileNotFoundException, IOException, URISyntaxException { + CSVReader csv = new CSVReader(new FileInputStream(source)); + csv.readHeaders(); + FHIRToolingClient client = new FHIRToolingClient("https://cts.nlm.nih.gov/fhir", username, password); + int i = 0; + while (csv.line()) { + String oid = csv.cell("OID"); try { - System.out.println("Process " + f.getName()); - List vsl = importValueSet(TextFile.fileToBytes(f)); - for (ValueSet vs : vsl) { - if (vs.getId() != null) { - new JsonParser().compose(new FileOutputStream(Utilities.path(dest, "ValueSet-" + vs.getId() + ".json")), vs); - } + ValueSet vs = client.read(ValueSet.class, oid); + new JsonParser().compose(new FileOutputStream(Utilities.path(dest, "ValueSet-"+oid+".json")), vs); + i++; + if (i % 100 == 0) { + System.out.println(i); } } catch (Exception e) { - e.printStackTrace(); + System.out.println("Unable to fetch OID "+oid+": "+e.getMessage()); } } + System.out.println("Done. "+i+" ValueSets"); +// for (File f : new File(source).listFiles()) { +// try { +// System.out.println("Process " + f.getName()); +// List vsl = importValueSet(TextFile.fileToBytes(f)); +// for (ValueSet vs : vsl) { +// if (vs.getId() != null) { +// new JsonParser().compose(new FileOutputStream(Utilities.path(dest, "ValueSet-" + vs.getId() + ".json")), vs); +// } +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } } - private List importValueSet(byte[] source) throws Exception { - List res = new ArrayList(); - Element x = loadXml(new ByteArrayInputStream(source)).getDocumentElement(); - List vl = XMLUtil.getNamedChildren(x, "DescribedValueSet"); - for (Element v : vl) { - ValueSet vs = new ValueSet(); - vs.setId(v.getAttribute("ID")); - vs.setUrl("http://vsac.nlm.nih.gov/fhir/ValueSet/" + vs.getId()); - vs.getMeta().setSource("https://vsac.nlm.nih.gov/valueset/" + vs.getId() + "/expansion"); - vs.setVersion(v.getAttribute("version")); - vs.setTitle(v.getAttribute("displayName")); - vs.setName(Utilities.titleize(vs.getTitle()).replace(" ", "")); - Element d = XMLUtil.getNamedChild(v, "Purpose"); - if (d != null) { - vs.setDescription(d.getTextContent()); - } - Element s = XMLUtil.getNamedChild(v, "Status"); - if (s != null && "Active".equals(s.getTextContent())) { - vs.setStatus(PublicationStatus.ACTIVE); - } else { - vs.setStatus(PublicationStatus.DRAFT); - } - Element dt = XMLUtil.getNamedChild(v, "RevisionDate"); - if (dt != null) { - vs.getDateElement().setValueAsString(dt.getTextContent()); - } - - Element cl = XMLUtil.getNamedChild(v, "ConceptList"); - Element cc = XMLUtil.getFirstChild(cl); - - while (cc != null) { - String code = cc.getAttribute("code"); - String display = cc.getAttribute("displayName"); - String csoid = cc.getAttribute("codeSystem"); - String csver = cc.getAttribute("codeSystemVersion"); - String url = context.oid2Uri(csoid); - if (url == null) { - url = "urn:oid:" + csoid; - } - csver = fixVersionforSystem(url, csver); - ConceptSetComponent inc = getInclude(vs, url, csver); - inc.addConcept().setCode(code).setDisplay(display); - cc = XMLUtil.getNextSibling(cc); - } - - res.add(vs); - } - return res; - } +// private List importValueSet(byte[] source) throws Exception { +// List res = new ArrayList(); +// Element x = loadXml(new ByteArrayInputStream(source)).getDocumentElement(); +// List vl = XMLUtil.getNamedChildren(x, "DescribedValueSet"); +// for (Element v : vl) { +// ValueSet vs = new ValueSet(); +// vs.setId(v.getAttribute("ID")); +// vs.setUrl("http://cts.nlm.nih.gov/fhir/ValueSet/" + vs.getId()); +// vs.getMeta().setSource("https://vsac.nlm.nih.gov/valueset/" + vs.getId() + "/expansion"); +// vs.setVersion(v.getAttribute("version")); +// vs.setTitle(v.getAttribute("displayName")); +// vs.setName(Utilities.titleize(vs.getTitle()).replace(" ", "")); +// Element d = XMLUtil.getNamedChild(v, "Purpose"); +// if (d != null) { +// vs.setDescription(d.getTextContent()); +// } +// Element s = XMLUtil.getNamedChild(v, "Status"); +// if (s != null && "Active".equals(s.getTextContent())) { +// vs.setStatus(PublicationStatus.ACTIVE); +// } else { +// vs.setStatus(PublicationStatus.DRAFT); +// } +// Element dt = XMLUtil.getNamedChild(v, "RevisionDate"); +// if (dt != null) { +// vs.getDateElement().setValueAsString(dt.getTextContent()); +// } +// +// Element cl = XMLUtil.getNamedChild(v, "ConceptList"); +// Element cc = XMLUtil.getFirstChild(cl); +// +// while (cc != null) { +// String code = cc.getAttribute("code"); +// String display = cc.getAttribute("displayName"); +// String csoid = cc.getAttribute("codeSystem"); +// String csver = cc.getAttribute("codeSystemVersion"); +// String url = context.oid2Uri(csoid); +// if (url == null) { +// url = "urn:oid:" + csoid; +// } +// csver = fixVersionforSystem(url, csver); +// ConceptSetComponent inc = getInclude(vs, url, csver); +// inc.addConcept().setCode(code).setDisplay(display); +// cc = XMLUtil.getNextSibling(cc); +// } +// +// res.add(vs); +// } +// return res; +// } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 6192b70a3..9242d42fd 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -78,6 +78,9 @@ import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Enumerations.BindingStrength; import org.hl7.fhir.r5.model.Enumerations.FHIRVersion; import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.ExpressionNode; +import org.hl7.fhir.r5.model.ExpressionNode.Kind; +import org.hl7.fhir.r5.model.ExpressionNode.Operation; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.IntegerType; @@ -99,6 +102,8 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.renderers.TerminologyRenderer; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; +import org.hl7.fhir.r5.utils.FHIRLexer; +import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.TranslatingUtilities; import org.hl7.fhir.r5.utils.XVerExtensionManager; @@ -200,7 +205,31 @@ public class ProfileUtilities extends TranslatingUtilities { } } - + public static class ElementChoiceGroup { + private Row row; + private String name; + private boolean mandatory; + private List elements = new ArrayList<>(); + + public ElementChoiceGroup(String name, boolean mandatory) { + super(); + this.name = name; + this.mandatory = mandatory; + } + public Row getRow() { + return row; + } + public List getElements() { + return elements; + } + public void setRow(Row row) { + this.row = row; + } + public String getName() { + return name; + } + } + private static final int MAX_RECURSION_LIMIT = 10; public class ExtensionContext { @@ -268,6 +297,7 @@ public class ProfileUtilities extends TranslatingUtilities { // note that ProfileUtilities are used re-entrantly internally, so nothing with process state can be here private final IWorkerContext context; + private FHIRPathEngine fpe; private List messages; private List snapshotStack = new ArrayList(); private ProfileKnowledgeProvider pkp; @@ -284,6 +314,9 @@ public class ProfileUtilities extends TranslatingUtilities { this.context = context; this.messages = messages; this.pkp = pkp; + if (context != null) { + this.fpe = new FHIRPathEngine(context, this); + } } public static class UnusedTracker { @@ -538,6 +571,7 @@ public class ProfileUtilities extends TranslatingUtilities { } processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size()-1, derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size()-1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, new ArrayList(), base); + checkGroupConstraints(derived); if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) { for (ElementDefinition e : diff.getElement()) { if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) { @@ -666,6 +700,81 @@ public class ProfileUtilities extends TranslatingUtilities { derived.clearUserData("profileutils.snapshot.generating"); } + private void checkGroupConstraints(StructureDefinition derived) { + List toRemove = new ArrayList<>(); +// List processed = new ArrayList<>(); + for (ElementDefinition element : derived.getSnapshot().getElement()) { + if (!toRemove.contains(element) && !element.hasSlicing() && !"0".equals(element.getMax())) { + checkForChildrenInGroup(derived, toRemove, element); + } + } + derived.getSnapshot().getElement().removeAll(toRemove); + } + + public void checkForChildrenInGroup(StructureDefinition derived, List toRemove, ElementDefinition element) throws Error { + List children = getChildren(derived, element); + List groups = readChoices(element, children); + for (ElementChoiceGroup group : groups) { + System.out.println(children); + String mandated = null; + Set names = new HashSet<>(); + for (ElementDefinition ed : children) { + String name = tail(ed.getPath()); + if (names.contains(name)) { + throw new Error("huh?"); + } else { + names.add(name); + } + if (group.getElements().contains(name)) { + if (ed.getMin() == 1) { + if (mandated == null) { + mandated = name; + } else { + throw new Error("Error: there are two mandatory elements in "+derived.getUrl()+" when there can only be one: "+mandated+" and "+name); + } + } + } + } + if (mandated != null) { + for (ElementDefinition ed : children) { + String name = tail(ed.getPath()); + if (group.getElements().contains(name) && !mandated.equals(name)) { + ed.setMax("0"); + addAllChildren(derived, ed, toRemove); + } + } + } + } + } + + private List getChildren(StructureDefinition derived, ElementDefinition element) { + List elements = derived.getSnapshot().getElement(); + int index = elements.indexOf(element) + 1; + String path = element.getPath()+"."; + List list = new ArrayList<>(); + while (index < elements.size()) { + ElementDefinition e = elements.get(index); + String p = e.getPath(); + if (p.startsWith(path) && !e.hasSliceName()) { + if (!p.substring(path.length()).contains(".")) { + list.add(e); + } + index++; + } else { + break; + } + } + return list; + } + + private void addAllChildren(StructureDefinition derived, ElementDefinition element, List toRemove) { + List children = getChildList(derived, element); + for (ElementDefinition child : children) { + toRemove.add(child); + addAllChildren(derived, child, toRemove); + } + } + private void checkDifferential(List elements, String type, String url) { boolean first = true; for (ElementDefinition ed : elements) { @@ -3620,12 +3729,17 @@ public class ProfileUtilities extends TranslatingUtilities { } Row currRow = row; + List groups = readChoices(element, children); boolean isExtension = Utilities.existsInList(tail(element.getPath()), "extension", "modifierExtension"); for (ElementDefinition child : children) { - if (!child.hasSliceName()) + if (!child.hasSliceName()) { currRow = row; - if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) - currRow = genElement(defPath, gen, currRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport); + } + Row childRow = chooseChildRowByGroup(gen, currRow, groups, child, element, isConstraintMode); + + if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) { + currRow = genElement(defPath, gen, childRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport); + } } // if (!snapshot && (extensions == null || !extensions)) // for (ElementDefinition child : children) @@ -3639,6 +3753,34 @@ public class ProfileUtilities extends TranslatingUtilities { return slicingRow; } + private Row chooseChildRowByGroup(HierarchicalTableGenerator gen, Row row, List groups, ElementDefinition element, ElementDefinition parent, boolean isConstraintMode) { + String name = tail(element.getPath()); + for (ElementChoiceGroup grp : groups) { + if (grp.getElements().contains(name)) { + if (grp.getRow() == null) { + grp.setRow(makeChoiceElementRow(gen, row, grp, parent, isConstraintMode)); + } + return grp.getRow(); + } + } + return row; + } + + private Row makeChoiceElementRow(HierarchicalTableGenerator gen, Row prow, ElementChoiceGroup grp, ElementDefinition parent, boolean isConstraintMode) { + Row row = gen.new Row(); + row.setAnchor(parent.getPath()+"-"+grp.getName()); + row.setColor(getRowColor(parent, isConstraintMode)); + row.setLineColor(1); + row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + row.getCells().add(gen.new Cell(null, null, "(Choice of one)", "", null)); + row.getCells().add(gen.new Cell()); + row.getCells().add(gen.new Cell(null, null, (grp.mandatory ? "1" : "0")+"..1", "", null)); + row.getCells().add(gen.new Cell()); + row.getCells().add(gen.new Cell()); + prow.getSubRows().add(row); + return row; + } + public Cell genElementNameCell(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, boolean ext, UnusedTracker used, String ref, String sName) throws IOException { @@ -3985,7 +4127,7 @@ public class ProfileUtilities extends TranslatingUtilities { } if (root) { if (profile.getAbstract()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.addPiece(gen.new Piece(null, "This is an abstract profile", null)); } } @@ -3993,10 +4135,10 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\""+buildJson(definition.getFixed())+"\"", null).addStyle("color: darkgreen"))); } else { if (definition != null && definition.hasShort()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.addPiece(checkForNoChange(definition.getShortElement(), gen.new Piece(null, gt(definition.getShortElement()), null))); } else if (fallback != null && fallback.hasShort()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.addPiece(gen.new Piece(null, gt(fallback.getShortElement()), null).addStyle("opacity: 0.5")); } if (url != null) { @@ -4041,7 +4183,7 @@ public class ProfileUtilities extends TranslatingUtilities { } if (definition.hasSlicing()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(gen.new Piece(null, translate("sd.table", "Slice")+": ", null).addStyle("font-weight:bold")); c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); } @@ -4095,7 +4237,7 @@ public class ProfileUtilities extends TranslatingUtilities { } if (definition.hasFixed()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold"))); if (!useTableForFixedValues || definition.getFixed().isPrimitive()) { String s = buildJson(definition.getFixed()); @@ -4113,7 +4255,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(p); } } else if (definition.hasPattern()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, translate("sd.table", "Required Pattern")+": ", null).addStyle("font-weight:bold"))); if (!useTableForFixedValues || definition.getPattern().isPrimitive()) c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); @@ -4123,13 +4265,13 @@ public class ProfileUtilities extends TranslatingUtilities { } } else if (definition.hasExample()) { for (ElementDefinitionExampleComponent ex : definition.getExample()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, translate("sd.table", "Example")+("".equals("General")? "" : " "+ex.getLabel())+": ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); } } if (definition.hasMaxLength() && definition.getMaxLength()!=0) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); } @@ -4367,7 +4509,7 @@ public class ProfileUtilities extends TranslatingUtilities { } if (definition.hasSlicing()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(gen.new Piece(null, "Slice: ", null).addStyle("font-weight:bold")); c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); } @@ -4389,12 +4531,16 @@ public class ProfileUtilities extends TranslatingUtilities { } } for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey()+": ", null).addStyle("font-weight:bold"))); - c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null))); + if (inv.getHumanElement().hasExtension("http://hl7.org/fhir/StructureDefinition/rendering-markdown")) { + c.addMarkdown(inv.getHumanElement().getExtensionString("http://hl7.org/fhir/StructureDefinition/rendering-markdown")); + } else { + c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getHuman(), null))); + } } if (definition.hasFixed()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight:bold"))); String s = buildJson(definition.getFixed()); String link = null; @@ -4402,18 +4548,18 @@ public class ProfileUtilities extends TranslatingUtilities { link = pkp.getLinkForUrl(corePath, s); c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); } else if (definition.hasPattern()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "Required Pattern: ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); } else if (definition.hasExample()) { for (ElementDefinitionExampleComponent ex : definition.getExample()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, "Example'"+("".equals("General")? "" : " "+ex.getLabel()+"'")+": ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); } } if (definition.hasMaxLength() && definition.getMaxLength()!=0) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen"))); } @@ -4434,14 +4580,14 @@ public class ProfileUtilities extends TranslatingUtilities { } } if (definition.hasDefinition()) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(gen.new Piece(null, "Definition: ", null).addStyle("font-weight:bold")); c.addPiece(gen.new Piece("br")); c.addMarkdown(definition.getDefinition()); // c.getPieces().add(checkForNoChange(definition.getCommentElement(), gen.new Piece(null, definition.getComment(), null))); } if (definition.getComment()!=null) { - if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(gen.new Piece(null, "Comments: ", null).addStyle("font-weight:bold")); c.addPiece(gen.new Piece("br")); c.addMarkdown(definition.getComment()); @@ -5935,6 +6081,64 @@ public class ProfileUtilities extends TranslatingUtilities { } + public List readChoices(ElementDefinition ed, List children) { + List result = new ArrayList<>(); + for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { + ElementChoiceGroup grp = processConstraint(children, c); + if (grp != null) { + result.add(grp); + } + } + return result; + } + + private ElementChoiceGroup processConstraint(List children, ElementDefinitionConstraintComponent c) { + if (!c.hasExpression()) { + return null; + } + ExpressionNode expr = fpe.parse(c.getExpression()); + if (expr.getKind() != Kind.Group || expr.getOpNext() == null || !(expr.getOperation() == Operation.Equals || expr.getOperation() == Operation.LessOrEqual)) { + return null; + } + ExpressionNode n1 = expr.getGroup(); + ExpressionNode n2 = expr.getOpNext(); + if (n2.getKind() != Kind.Constant || n2.getInner() != null || n2.getOpNext() != null || !"1".equals(n2.getConstant().primitiveValue())) { + return null; + } + ElementChoiceGroup grp = new ElementChoiceGroup(c.getKey(), expr.getOperation() == Operation.Equals); + while (n1 != null) { + if (n1.getKind() != Kind.Name || n1.getInner() != null) { + return null; + } + grp.elements.add(n1.getName()); + if (n1.getOperation() == null || n1.getOperation() == Operation.Union) { + n1 = n1.getOpNext(); + } else { + return null; + } + } + int total = 0; + for (String n : grp.elements) { + boolean found = false; + for (ElementDefinition child : children) { + String name = tail(child.getPath()); + if (n.equals(name)) { + found = true; + if (!"0".equals(child.getMax())) { + total++; + } + } + } + if (!found) { + return null; + } + } + if (total <= 1) { + return null; + } + return grp; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java index 13ceb36c3..c7f140781 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java @@ -171,8 +171,9 @@ private Map userData; List children = new ArrayList(); listChildren(children); for (Property c : children) - if (c.getName().equals(name)) + if (c.getName().equals(name) || c.getName().equals(name+"[x]")) { return c; + } return null; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index a51bb8a9e..b5dce979b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -14,10 +14,16 @@ import org.hl7.fhir.r5.model.Annotation; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.BaseDateTimeType; import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ContactPoint; +import org.hl7.fhir.r5.model.DataRequirement; +import org.hl7.fhir.r5.model.DataRequirement.DataRequirementCodeFilterComponent; +import org.hl7.fhir.r5.model.DataRequirement.DataRequirementDateFilterComponent; +import org.hl7.fhir.r5.model.DataRequirement.DataRequirementSortComponent; +import org.hl7.fhir.r5.model.DataRequirement.SortDirection; import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.DateTimeType; @@ -778,6 +784,102 @@ public class DataRenderer extends Renderer { x.addText(!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay()); } + public void renderDataRequirement(XhtmlNode x, DataRequirement dr) { + XhtmlNode tbl = x.table("grid"); + XhtmlNode tr = tbl.tr(); + XhtmlNode td = tr.td().colspan("2"); + td.b().tx("Type"); + td.tx(": "); + StructureDefinition sd = context.getWorker().fetchTypeDefinition(dr.getType().toCode()); + if (sd != null && sd.hasUserData("path")) { + td.ah(sd.getUserString("path")).tx(dr.getType().toCode()); + } else { + td.tx(dr.getType().toCode()); + } + if (dr.hasProfile()) { + td.tx(" ("); + boolean first = true; + for (CanonicalType p : dr.getProfile()) { + if (first) first = false; else td.tx(" | "); + sd = context.getWorker().fetchResource(StructureDefinition.class, p.getValue()); + if (sd != null && sd.hasUserData("path")) { + td.ah(sd.getUserString("path")).tx(sd.present()); + } else { + td.tx(p.asStringValue()); + } + } + td.tx(")"); + } + if (dr.hasSubject()) { + tr = tbl.tr(); + td = tr.td().colspan("2"); + td.b().tx("Subject"); + if (dr.hasSubjectReference()) { + renderReference(td, dr.getSubjectReference()); + } else { + renderCodeableConcept(td, dr.getSubjectCodeableConcept()); + } + } + if (dr.hasCodeFilter() || dr.hasDateFilter()) { + tr = tbl.tr().backgroundColor("#efefef"); + tr.td().tx("Filter"); + tr.td().tx("Value"); + } + for (DataRequirementCodeFilterComponent cf : dr.getCodeFilter()) { + tr = tbl.tr(); + if (cf.hasPath()) { + tr.td().tx(cf.getPath()); + } else { + tr.td().tx("Search on " +cf.getSearchParam()); + } + if (cf.hasValueSet()) { + td = tr.td(); + td.tx("In ValueSet "); + render(td, cf.getValueSetElement()); + } else { + boolean first = true; + td = tr.td(); + td.tx("One of these codes: "); + for (Coding c : cf.getCode()) { + if (first) first = false; else td.tx(", "); + render(td, c); + } + } + } + for (DataRequirementDateFilterComponent cf : dr.getDateFilter()) { + tr = tbl.tr(); + if (cf.hasPath()) { + tr.td().tx(cf.getPath()); + } else { + tr.td().tx("Search on " +cf.getSearchParam()); + } + render(tr.td(), cf.getValue()); + } + if (dr.hasSort() || dr.hasLimit()) { + tr = tbl.tr(); + td = tr.td().colspan("2"); + if (dr.hasLimit()) { + td.b().tx("Limit"); + td.tx(": "); + td.tx(dr.getLimit()); + if (dr.hasSort()) { + td.tx(", "); + } + } + if (dr.hasSort()) { + td.b().tx("Sort"); + td.tx(": "); + boolean first = true; + for (DataRequirementSortComponent p : dr.getSort()) { + if (first) first = false; else td.tx(" | "); + td.tx(p.getDirection() == SortDirection.ASCENDING ? "+" : "-"); + td.tx(p.getPath()); + } + } + } + } + + private String displayTiming(Timing s) throws FHIRException { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); if (s.hasCode()) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java index fc8e6436b..d6fcf528e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ParametersRenderer.java @@ -31,13 +31,19 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode; public class ParametersRenderer extends ResourceRenderer { + public ParametersRenderer(RenderingContext context) { + super(context); + } + public ParametersRenderer(RenderingContext context, ResourceContext rcontext) { super(context, rcontext); } - @Override public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { + x.h2().tx("Parameters"); + XhtmlNode tbl = x.table("grid"); + params(tbl, ((Parameters) r).getParameter(), 0); return false; } @@ -87,7 +93,7 @@ public class ParametersRenderer extends ResourceRenderer { } } else if (p.has("part")) { tr.td(); - PropertyWrapper pw = getProperty(p, "parameter"); + PropertyWrapper pw = getProperty(p, "part"); paramsW(tbl, pw.getValues(), 1); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java index 9b5841bb0..88870997d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java @@ -27,6 +27,7 @@ import org.hl7.fhir.r5.model.CodeableReference; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ContactDetail; import org.hl7.fhir.r5.model.ContactPoint; +import org.hl7.fhir.r5.model.DataRequirement; import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Dosage; @@ -82,6 +83,7 @@ import org.w3c.dom.Element; public class ProfileDrivenRenderer extends ResourceRenderer { private Set containedIds = new HashSet<>(); + private boolean hasExtensions; public ProfileDrivenRenderer(RenderingContext context, ResourceContext rcontext) { super(context, rcontext); @@ -108,12 +110,13 @@ public class ProfileDrivenRenderer extends ResourceRenderer { System.out.println("hah!"); } containedIds.clear(); + hasExtensions = false; generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0); } catch (Exception e) { e.printStackTrace(); x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage()); } - return false; + return hasExtensions; } @Override @@ -378,6 +381,9 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } } else if (e instanceof Resource) { return; + } else if (e instanceof DataRequirement) { + DataRequirement p = (DataRequirement) e; + renderDataRequirement(x, p); } else if (e instanceof ElementDefinition) { x.tx("todo-bundle"); } else if (e != null && !(e instanceof Attachment) && !(e instanceof Narrative) && !(e instanceof Meta)) { @@ -386,7 +392,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { throw new NotImplementedException("type "+e.getClass().getName()+" not handled yet, and no structure found"); else generateByProfile(res, sd, ew, sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep(), - getChildrenForPath(sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep().getPath()), x, path, showCodeDetails, indent + 1); + getChildrenForPath(sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep().getPath()), x, e.fhirType(), showCodeDetails, indent + 1); } } @@ -612,65 +618,98 @@ public class ProfileDrivenRenderer extends ResourceRenderer { for (PropertyWrapper p : splitExtensions(profile, e.children())) { if (p.hasValues()) { ElementDefinition child = getElementDefinition(children, path+"."+p.getName(), p); + if (child == null) { + child = p.getElementDefinition(); + } if (child != null) { - Map displayHints = readDisplayHints(child); - if ("DomainResource.contained".equals(child.getBase().getPath())) { + generateElementByProfile(res, profile, allElements, x, path, showCodeDetails, indent, p, child); + } + } + } + } + } + + public void generateElementByProfile(ResourceWrapper res, StructureDefinition profile, List allElements, XhtmlNode x, String path, + boolean showCodeDetails, int indent, PropertyWrapper p, ElementDefinition child) throws UnsupportedEncodingException, IOException, EOperationOutcome { + Map displayHints = readDisplayHints(child); + if ("DomainResource.contained".equals(child.getBase().getPath())) { // if (p.getValues().size() > 0 && child != null) { // for (BaseWrapper v : p.getValues()) { // x.an(v.get("id").primitiveValue()); // } // } - } else if (!exemptFromRendering(child)) { - List grandChildren = getChildrenForPath(allElements, path+"."+p.getName()); - filterGrandChildren(grandChildren, path+"."+p.getName(), p); - if (p.getValues().size() > 0) { - if (isPrimitive(child)) { - XhtmlNode para = x.para(); - String name = p.getName(); - if (name.endsWith("[x]")) - name = name.substring(0, name.length() - 3); - if (showCodeDetails || !isDefaultValue(displayHints, p.getValues())) { - para.b().addText(name); - para.tx(": "); - if (renderAsList(child) && p.getValues().size() > 1) { - XhtmlNode list = x.ul(); - for (BaseWrapper v : p.getValues()) - renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent); - } else { - boolean first = true; - for (BaseWrapper v : p.getValues()) { - if (first) - first = false; - else - para.tx(", "); - renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent); - } - } - } - } else if (canDoTable(path, p, grandChildren)) { - x.addTag(getHeader()).addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName())))); - XhtmlNode tbl = x.table( "grid"); - XhtmlNode tr = tbl.tr(); - tr.td().tx("-"); // work around problem with empty table rows - addColumnHeadings(tr, grandChildren); - for (BaseWrapper v : p.getValues()) { - if (v != null) { - tr = tbl.tr(); - tr.td().tx("*"); // work around problem with empty table rows - addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent); - } - } - } else { - for (BaseWrapper v : p.getValues()) { - if (v != null) { - XhtmlNode bq = x.addTag("blockquote"); - bq.para().b().addText(p.getName()); - generateByProfile(res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1); - } - } + } else if (!exemptFromRendering(child)) { + if (isExtension(p)) { + hasExtensions = true; + } + List grandChildren = getChildrenForPath(allElements, path+"."+p.getName()); + filterGrandChildren(grandChildren, path+"."+p.getName(), p); + if (p.getValues().size() > 0) { + if (isPrimitive(child)) { + XhtmlNode para = x.para(); + String name = p.getName(); + if (name.endsWith("[x]")) + name = name.substring(0, name.length() - 3); + if (showCodeDetails || !isDefaultValue(displayHints, p.getValues())) { + para.b().addText(name); + para.tx(": "); + if (renderAsList(child) && p.getValues().size() > 1) { + XhtmlNode list = x.ul(); + for (BaseWrapper v : p.getValues()) + renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent); + } else { + boolean first = true; + for (BaseWrapper v : p.getValues()) { + if (first) + first = false; + else + para.tx(", "); + renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent); + } + } + } + } else if (canDoTable(path, p, grandChildren)) { + x.addTag(getHeader()).addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName())))); + XhtmlNode tbl = x.table( "grid"); + XhtmlNode tr = tbl.tr(); + tr.td().tx("-"); // work around problem with empty table rows + addColumnHeadings(tr, grandChildren); + for (BaseWrapper v : p.getValues()) { + if (v != null) { + tr = tbl.tr(); + tr.td().tx("*"); // work around problem with empty table rows + addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent); + } + } + } else if (isExtension(p)) { + for (BaseWrapper v : p.getValues()) { + if (v != null) { + PropertyWrapper vp = v.getChildByName("value"); + PropertyWrapper ev = v.getChildByName("extension"); + if (vp.hasValues()) { + BaseWrapper vv = vp.value(); + XhtmlNode para = x.para(); + para.b().addText(p.getStructure().present()); + para.tx(": "); + renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent); + } else if (ev.hasValues()) { + XhtmlNode bq = x.addTag("blockquote"); + bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName()); + for (BaseWrapper vv : ev.getValues()) { + StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension"); + List children = getChildrenForPath(ex.getSnapshot().getElement(), "Extension"); + generateByProfile(res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1); } } } + } + } else { + for (BaseWrapper v : p.getValues()) { + if (v != null) { + XhtmlNode bq = x.addTag("blockquote"); + bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName()); + generateByProfile(res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1); + } } } } @@ -699,6 +738,9 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } private boolean canDoTable(String path, PropertyWrapper p, List grandChildren) { + if (isExtension(p)) { + return false; + } for (ElementDefinition e : grandChildren) { List values = getValues(path, p, e); if (values.size() > 1 || !isPrimitive(e) || !canCollapse(e)) @@ -707,6 +749,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer { return true; } + public boolean isExtension(PropertyWrapper p) { + return p.getName().contains("extension["); + } + private boolean canCollapse(ElementDefinition e) { // we can collapse any data type @@ -792,10 +838,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer { if (url.startsWith("http://hl7.org/fhir") && !url.startsWith("http://hl7.org/fhir/us")) throw new DefinitionException("unknown extension "+url); // System.out.println("unknown extension "+url); - pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", p.getTypeCode(), p.getDefinition(), p.getMinCardinality(), p.getMaxCardinality(), ex)); + pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", p.getTypeCode(), p.getDefinition(), p.getMinCardinality(), p.getMaxCardinality(), ex), ed.getSnapshot().getElementFirstRep()); } else { ElementDefinition def = ed.getSnapshot().getElement().get(0); - pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", "Extension", def.getDefinition(), def.getMin(), def.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(def.getMax()), ex)); + pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", "Extension", def.getDefinition(), def.getMin(), def.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(def.getMax()), ex), ed.getSnapshot().getElementFirstRep()); ((PropertyWrapperDirect) pe).getWrapped().setStructure(ed); } results.add(pe); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java index 9fb41cc12..5935729b4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java @@ -72,6 +72,9 @@ public class RendererFactory { if ("OperationOutcome".equals(resourceName)) { return new OperationOutcomeRenderer(context); } + if ("Parameters".equals(resourceName)) { + return new ParametersRenderer(context); + } return new ProfileDrivenRenderer(context); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java index b811ac7fe..fef5851a2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/BaseWrappers.java @@ -8,6 +8,7 @@ import java.util.List; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.Narrative.NarrativeStatus; import org.hl7.fhir.r5.renderers.ResourceRenderer; @@ -28,6 +29,7 @@ public class BaseWrappers { public int getMinCardinality(); public int getMaxCardinality(); public StructureDefinition getStructure(); + public ElementDefinition getElementDefinition(); public BaseWrapper value(); public ResourceWrapper getAsResource(); public String fhirType(); @@ -87,7 +89,7 @@ public class BaseWrappers { @Override public boolean has(String name) { for (PropertyWrapper p : children()) { - if (p.getName().equals(name)) { + if (p.getName().equals(name) || p.getName().equals(name+"[x]") ) { return p.hasValues(); } } @@ -97,7 +99,7 @@ public class BaseWrappers { @Override public Base get(String name) throws UnsupportedEncodingException, FHIRException, IOException { for (PropertyWrapper p : children()) { - if (p.getName().equals(name)) { + if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { if (p.hasValues()) { return p.getValues().get(0).getBase(); } else { @@ -111,7 +113,7 @@ public class BaseWrappers { @Override public List children(String name) throws UnsupportedEncodingException, FHIRException, IOException { for (PropertyWrapper p : children()) { - if (p.getName().equals(name)) { + if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { List res = new ArrayList<>(); for (BaseWrapper b : p.getValues()) { res.add(b); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java index da7d83da6..3fd8d6a7c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java @@ -206,6 +206,11 @@ public class DOMWrappers { return getTypeCode(); } + @Override + public ElementDefinition getElementDefinition() { + return definition; + } + } public static class ResourceWrapperElement extends WrapperBaseImpl implements ResourceWrapper { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java index 3cbe12568..7ec2aeb5b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DirectWrappers.java @@ -8,6 +8,7 @@ import java.util.List; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.Encounter; import org.hl7.fhir.r5.model.Narrative.NarrativeStatus; import org.hl7.fhir.r5.model.Patient; @@ -29,6 +30,7 @@ public class DirectWrappers { public static class PropertyWrapperDirect extends RendererWrapperImpl implements PropertyWrapper { private Property wrapped; private List list; + private ElementDefinition ed; public PropertyWrapperDirect(RenderingContext context, Property wrapped) { super(context); @@ -37,6 +39,14 @@ public class DirectWrappers { this.wrapped = wrapped; } + public PropertyWrapperDirect(RenderingContext context, Property wrapped, ElementDefinition ed) { + super(context); + if (wrapped == null) + throw new Error("wrapped == null"); + this.wrapped = wrapped; + this.ed = ed; + } + @Override public String getName() { return wrapped.getName(); @@ -106,6 +116,11 @@ public class DirectWrappers { public String fhirType() { return wrapped.getTypeCode(); } + + @Override + public ElementDefinition getElementDefinition() { + return ed; + } } public static class BaseWrapperDirect extends WrapperBaseImpl implements BaseWrapper { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java index b21cd002b..2ca58caf5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ElementWrappers.java @@ -58,7 +58,7 @@ public class ElementWrappers { throw new FHIRException(e.getMessage(), e); } if (context.getParser() == null) { - System.out.println("Noe version specific parser provided"); + System.out.println("No version specific parser provided"); } if (context.getParser() == null) { throw new Error("No type parser provided to renderer context"); @@ -324,6 +324,11 @@ public class ElementWrappers { return getTypeCode(); } + @Override + public ElementDefinition getElementDefinition() { + return definition; + } + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index 7ce5d015e..e16776f40 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -319,6 +319,20 @@ public class FHIRPathEngine { } } + public FHIRPathEngine(IWorkerContext worker, ProfileUtilities utilities) { + super(); + this.worker = worker; + profileUtilities = utilities; + for (StructureDefinition sd : worker.getStructures()) { + if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() != StructureDefinitionKind.LOGICAL) { + allTypes.put(sd.getName(), sd); + } + if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { + primitiveTypes.add(sd.getName()); + } + } + } + // --- 3 methods to override in children ------------------------------------------------------- // if you don't override, it falls through to the using the base reference implementation diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java index 22a5d0c54..0a46a7fcf 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java @@ -104,17 +104,17 @@ public class NPMPackageGenerator { private NpmPackageIndexBuilder indexer; - public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date) throws FHIRException, IOException { + public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, boolean notForPublication) throws FHIRException, IOException { super(); this.destFile = destFile; start(); List fhirVersion = new ArrayList<>(); for (Enumeration v : ig.getFhirVersion()) fhirVersion.add(v.asStringValue()); - buildPackageJson(canonical, kind, url, date, ig, fhirVersion); + buildPackageJson(canonical, kind, url, date, ig, fhirVersion, notForPublication); } - public static NPMPackageGenerator subset(NPMPackageGenerator master, String destFile, String id, String name, Date date) throws FHIRException, IOException { + public static NPMPackageGenerator subset(NPMPackageGenerator master, String destFile, String id, String name, Date date, boolean notForPublication) throws FHIRException, IOException { JsonObject p = master.packageJ.deepCopy(); p.remove("name"); p.addProperty("name", id); @@ -122,24 +122,30 @@ public class NPMPackageGenerator { p.addProperty("type", PackageType.SUBSET.getCode()); p.remove("title"); p.addProperty("title", name); + if (notForPublication) { + p.addProperty("notForPublication", true); + } - return new NPMPackageGenerator(destFile, p, date); + return new NPMPackageGenerator(destFile, p, date, notForPublication); } - public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, List fhirVersion) throws FHIRException, IOException { + public NPMPackageGenerator(String destFile, String canonical, String url, PackageType kind, ImplementationGuide ig, Date date, List fhirVersion, boolean notForPublication) throws FHIRException, IOException { super(); this.destFile = destFile; start(); - buildPackageJson(canonical, kind, url, date, ig, fhirVersion); + buildPackageJson(canonical, kind, url, date, ig, fhirVersion, notForPublication); } - public NPMPackageGenerator(String destFile, JsonObject npm, Date date) throws FHIRException, IOException { + public NPMPackageGenerator(String destFile, JsonObject npm, Date date, boolean notForPublication) throws FHIRException, IOException { super(); String dt = new SimpleDateFormat("yyyyMMddHHmmss").format(date); packageJ = npm; packageManifest = new JsonObject(); packageManifest.addProperty("version", npm.get("version").getAsString()); packageManifest.addProperty("date", dt); + if (notForPublication) { + packageManifest.addProperty("notForPublication", true); + } npm.addProperty("date", dt); packageManifest.addProperty("name", npm.get("name").getAsString()); this.destFile = destFile; @@ -152,19 +158,23 @@ public class NPMPackageGenerator { } } - private void buildPackageJson(String canonical, PackageType kind, String web, Date date, ImplementationGuide ig, List fhirVersion) throws FHIRException, IOException { + private void buildPackageJson(String canonical, PackageType kind, String web, Date date, ImplementationGuide ig, List fhirVersion, boolean notForPublication) throws FHIRException, IOException { String dtHuman = new SimpleDateFormat("EEE, MMM d, yyyy HH:mmZ", new Locale("en", "US")).format(date); String dt = new SimpleDateFormat("yyyyMMddHHmmss").format(date); CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - if (!ig.hasPackageId()) + if (!ig.hasPackageId()) { b.append("packageId"); - if (!ig.hasVersion()) + } + if (!ig.hasVersion()) { b.append("version"); - if (!ig.hasFhirVersion()) + } + if (!ig.hasFhirVersion()) { b.append("fhirVersion"); - if (!ig.hasLicense()) + } + if (!ig.hasLicense()) { b.append("license"); + } for (ImplementationGuideDependsOnComponent d : ig.getDependsOn()) { if (!d.hasVersion()) { b.append("dependsOn.version("+d.getUri()+")"); @@ -177,14 +187,20 @@ public class NPMPackageGenerator { npm.addProperty("tools-version", ToolsVersion.TOOLS_VERSION); npm.addProperty("type", kind.getCode()); npm.addProperty("date", dt); - if (ig.hasLicense()) + if (ig.hasLicense()) { npm.addProperty("license", ig.getLicense().toCode()); + } npm.addProperty("canonical", canonical); + if (notForPublication) { + npm.addProperty("notForPublication", true); + } npm.addProperty("url", web); - if (ig.hasTitle()) + if (ig.hasTitle()) { npm.addProperty("title", ig.getTitle()); - if (ig.hasDescription()) + } + if (ig.hasDescription()) { npm.addProperty("description", ig.getDescription()+ " (built "+dtHuman+timezone()+")"); + } JsonArray vl = new JsonArray(); npm.add("fhirVersions", vl); @@ -205,8 +221,9 @@ public class NPMPackageGenerator { dep.addProperty(d.getPackageId(), d.getVersion()); } } - if (ig.hasPublisher()) + if (ig.hasPublisher()) { npm.addProperty("author", ig.getPublisher()); + } JsonArray m = new JsonArray(); for (ContactDetail t : ig.getContact()) { String email = email(t.getTelecom()); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java index c320f912f..9011cd929 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java @@ -11,22 +11,32 @@ import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.elementmodel.Manager; +import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; +import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Questionnaire; +import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.renderers.RendererFactory; import org.hl7.fhir.r5.renderers.ResourceRenderer; +import org.hl7.fhir.r5.renderers.utils.ElementWrappers; import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.ITypeParser; import org.hl7.fhir.r5.renderers.utils.RenderingContext.QuestionnaireRendererMode; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.test.NarrativeGenerationTests.TestTypeParser; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.xhtml.XhtmlComposer; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; +import org.hl7.fhir.utilities.xhtml.XhtmlParser; import org.hl7.fhir.utilities.xml.XMLUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -40,6 +50,15 @@ import org.xml.sax.SAXException; public class NarrativeGenerationTests { + public class TestTypeParser implements ITypeParser { + + @Override + public Base parseType(String xml, String type) throws FHIRFormatError, IOException, FHIRException { + return new org.hl7.fhir.r5.formats.XmlParser().parseType(xml, type); + } + + } + public static final String WINDOWS = "WINDOWS"; private static final String HEADER = ""+ @@ -57,11 +76,13 @@ public class NarrativeGenerationTests { public static class TestDetails { private String id; private boolean header; + private boolean meta; public TestDetails(Element test) { super(); id = test.getAttribute("id"); header = "true".equals(test.getAttribute("header")); + meta = "true".equals(test.getAttribute("meta")); } public String getId() { @@ -70,6 +91,10 @@ public class NarrativeGenerationTests { public boolean isHeader() { return header; + } + + public boolean isMeta() { + return meta; } } @@ -107,19 +132,30 @@ public class NarrativeGenerationTests { rc.setHeader(test.isHeader()); rc.setDefinitionsTarget("test.html"); rc.setTerminologyServiceOptions(TerminologyServiceOptions.defaults()); - IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-expected.xml"), new FileOutputStream(TestingUtilities.tempFile("narrative", test.getId() + "-expected.xml"))); - DomainResource source; - if (TestingUtilities.findTestResource("r5", "narrative", test.getId() + "-input.json")) { - source = (DomainResource) new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-input.json")); + rc.setParser(new TestTypeParser()); + Resource source; + if (TestingUtilities.findTestResource("r5", "narrative", test.getId() + ".json")) { + source = (Resource) new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".json")); } else { - source = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-input.xml")); + source = (Resource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".xml")); + } + + XhtmlNode x = RendererFactory.factory(source, rc).build(source); + String target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".html")); + String output = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER; + TextFile.stringToFile(target, TestingUtilities.tempFile("narrative", test.getId() + ".target.html")); + TextFile.stringToFile(output, TestingUtilities.tempFile("narrative", test.getId() + ".output.html")); + Assertions.assertTrue(output.equals(target), "Output does not match expected"); + + if (test.isMeta()) { + org.hl7.fhir.r5.elementmodel.Element e = Manager.parse(context, TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".xml"), FhirFormat.XML); + x = RendererFactory.factory(source, rc).render(new ElementWrappers.ResourceWrapperMetaElement(rc, e)); + + target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html")); + output = HEADER+new XhtmlComposer(true).compose(x)+FOOTER; + TextFile.stringToFile(output, TestingUtilities.tempFile("narrative", test.getId() + "-meta.output.html")); + Assertions.assertTrue(output.equals(target), "Output does not match expected (meta)"); } - DomainResource target = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-expected.xml")); - RendererFactory.factory(source, rc).render(source); - new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(TestingUtilities.tempFile("narrative", test.getId() + "-actual.xml")), source); - source = (DomainResource) new XmlParser().parse(new FileInputStream(TestingUtilities.tempFile("narrative", test.getId() + "-actual.xml"))); - String html = HEADER+new XhtmlComposer(true).compose(source.getText().getDiv())+FOOTER; - TextFile.stringToFile(html, TestingUtilities.tempFile("narrative", test.getId() + ".html")); - Assertions.assertTrue(source.equalsDeep(target), "Output does not match expected"); } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ResourceRoundTripTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ResourceRoundTripTests.java index 6406fb930..ba462e423 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ResourceRoundTripTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ResourceRoundTripTests.java @@ -13,6 +13,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.renderers.RendererFactory; @@ -20,6 +21,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.EOperationOutcome; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class ResourceRoundTripTests { @@ -52,4 +54,5 @@ public class ResourceRoundTripTests { if (result == null) throw new FHIRException("Bundle was null"); } + } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java index 75d298867..b87b60818 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/NpmPackage.java @@ -1091,6 +1091,10 @@ public class NpmPackage { } return true; } + + public boolean isNotForPublication() { + return JSONUtil.bool(npm, "notForPublication"); + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java index 419c88212..936bb7cdd 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java @@ -741,6 +741,12 @@ public class XhtmlNode implements IBaseXhtml { } + public XhtmlNode backgroundColor(String color) { + style("background-color: "+color); + return this; + } + + diff --git a/pom.xml b/pom.xml index b72385c45..611b9deb9 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 5.1.0 - 1.1.35 + 1.1.36-SNAPSHOT 5.6.2 3.0.0-M4 0.8.5 From f56235d5966bf37f76075b5c939c1ef191ace0bc Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 4 Sep 2020 23:52:52 +1000 Subject: [PATCH 2/2] rendering fixes (#334) * Fix up conversion for date problems * Mark packages as unsuitable for publication * rework rendering tests & fix bug in Parameters renderer * fix test case depedency