From 506dad87524f8bedab87c72ffe4ba4a0373e5bf1 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 13 Jun 2024 07:38:22 +1000 Subject: [PATCH] WIP: rewrite of rendering framework --- .../hl7/fhir/r5/renderers/DataRenderer.java | 1439 ++++++++++------- .../renderers/DiagnosticReportRenderer.java | 9 +- .../r5/renderers/ProfileDrivenRenderer.java | 3 +- .../org/hl7/fhir/r5/renderers/Renderer.java | 12 + .../fhir/r5/renderers/RendererFactory.java | 12 +- .../fhir/r5/renderers/ResourceRenderer.java | 491 +++--- .../StructureDefinitionRenderer.java | 53 +- .../hl7/fhir/r5/renderers/utils/Resolver.java | 9 +- .../r5/renderers/utils/ResourceElement.java | 113 +- .../test/rendering/ResourceElementTests.java | 5 +- 10 files changed, 1225 insertions(+), 921 deletions(-) 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 55e42979c..ddf06f9d3 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,8 +14,9 @@ import java.time.format.DateTimeFormatterBuilder; import java.time.format.FormatStyle; import java.time.format.SignStyle; import java.util.Currency; -import java.util.List; - +import java.util.List; + +import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; @@ -23,11 +24,13 @@ import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.Address; -import org.hl7.fhir.r5.model.Annotation; +import org.hl7.fhir.r5.model.Annotation; +import org.hl7.fhir.r5.model.Attachment; import org.hl7.fhir.r5.model.BackboneType; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.Base64BinaryType; -import org.hl7.fhir.r5.model.BaseDateTimeType; +import org.hl7.fhir.r5.model.BaseDateTimeType; +import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CodeSystem; @@ -45,7 +48,8 @@ import org.hl7.fhir.r5.model.DataRequirement.DataRequirementSortComponent; import org.hl7.fhir.r5.model.DataRequirement.SortDirection; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.DateTimeType; -import org.hl7.fhir.r5.model.DateType; +import org.hl7.fhir.r5.model.DateType; +import org.hl7.fhir.r5.model.Dosage; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Expression; @@ -54,17 +58,23 @@ import org.hl7.fhir.r5.model.ExtensionHelper; import org.hl7.fhir.r5.model.HumanName; import org.hl7.fhir.r5.model.HumanName.NameUse; import org.hl7.fhir.r5.model.IdType; -import org.hl7.fhir.r5.model.Identifier; -import org.hl7.fhir.r5.model.MarkdownType; +import org.hl7.fhir.r5.model.Identifier; +import org.hl7.fhir.r5.model.InstantType; +import org.hl7.fhir.r5.model.MarkdownType; +import org.hl7.fhir.r5.model.Meta; import org.hl7.fhir.r5.model.Money; -import org.hl7.fhir.r5.model.NamingSystem; +import org.hl7.fhir.r5.model.NamingSystem; +import org.hl7.fhir.r5.model.Narrative; import org.hl7.fhir.r5.model.Period; import org.hl7.fhir.r5.model.PrimitiveType; import org.hl7.fhir.r5.model.Quantity; -import org.hl7.fhir.r5.model.Range; -import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.Range; +import org.hl7.fhir.r5.model.Ratio; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.RelatedArtifact; import org.hl7.fhir.r5.model.Resource; -import org.hl7.fhir.r5.model.SampledData; +import org.hl7.fhir.r5.model.SampledData; +import org.hl7.fhir.r5.model.Signature; import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.Timing; @@ -77,11 +87,12 @@ import org.hl7.fhir.r5.model.UsageContext; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; -import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; -import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceWithReference; +import org.hl7.fhir.r5.renderers.utils.ResourceElement; import org.hl7.fhir.r5.terminologies.JurisdictionUtilities; import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; import org.hl7.fhir.r5.utils.ToolingExtensions; @@ -408,6 +419,10 @@ public class DataRenderer extends Renderer implements CodeResolver { public String gt(@SuppressWarnings("rawtypes") PrimitiveType value) { return context.getTranslated(value); } + + public String gt(ResourceElement value) { + return context.getTranslated(value); + } // -- 6. General purpose extension rendering ---------------------------------------------- @@ -429,6 +444,15 @@ public class DataRenderer extends Renderer implements CodeResolver { return element.hasModifierExtension(); } + public boolean hasRenderableExtensions(ResourceElement element) { + for (ResourceElement ext : element.extensions()) { + if (canRender(ext)) { + return true; + } + } + return false; + } + private String getExtensionLabel(Extension ext) { StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, ext.getUrl()); if (sd != null && ext.hasValue() && ext.getValue().isPrimitive() && sd.hasSnapshot()) { @@ -440,52 +464,77 @@ public class DataRenderer extends Renderer implements CodeResolver { } return null; } + + private String getExtensionLabel(ResourceElement ext) { + StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, ext.primitiveValue("url")); + if (sd != null && ext.has("value") && ext.child("value").isPrimitive() && sd.hasSnapshot()) { + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (Utilities.existsInList(ed.getPath(), "Extension", "Extension.value[x]") && ed.hasLabel()) { + return context.getTranslated(ed.getLabelElement()); + } + } + } + return null; + } private boolean canRender(Extension ext) { return getExtensionLabel(ext) != null; } - public void renderExtensionsInList(XhtmlNode ul, DataType element) throws FHIRFormatError, DefinitionException, IOException { - for (Extension ext : element.getExtension()) { + + private boolean canRender(ResourceElement ext) { + StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, ext.primitiveValue("url")); + if (sd != null && ext.has("value") && ext.isPrimitive("value") && sd.hasSnapshot()) { + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (Utilities.existsInList(ed.getPath(), "Extension", "Extension.value[x]") && ed.hasLabel()) { + return true; + } + } + } + return false; + } + + public void renderExtensionsInList(RenderingStatus status, XhtmlNode ul, ResourceElement element) throws FHIRFormatError, DefinitionException, IOException { + for (ResourceElement ext : element.extensions()) { if (canRender(ext)) { String lbl = getExtensionLabel(ext); XhtmlNode li = ul.li(); li.tx(lbl); li.tx(": "); - render(li, ext.getValue()); + renderDataType(status, li, ext.child("value")); } } } - public void renderExtensionsInList(XhtmlNode ul, BackboneType element) throws FHIRFormatError, DefinitionException, IOException { - for (Extension ext : element.getModifierExtension()) { - if (canRender(ext)) { - String lbl = getExtensionLabel(ext); - XhtmlNode li = ul.li(); - li = li.b(); - li.tx(lbl); - li.tx(": "); - render(li, ext.getValue()); - } else { - // somehow have to do better than this - XhtmlNode li = ul.li(); - li.b().tx(context.formatPhrase(RenderingContext.DATA_REND_UNRD_EX)); - } - } - for (Extension ext : element.getExtension()) { - if (canRender(ext)) { - String lbl = getExtensionLabel(ext); - XhtmlNode li = ul.li(); - li.tx(lbl); - li.tx(": "); - render(li, ext.getValue()); - } - } - } - - public void renderExtensionsInText(XhtmlNode div, DataType element, String sep) throws FHIRFormatError, DefinitionException, IOException { +// public void renderExtensionsInList(XhtmlNode ul, BackboneType element) throws FHIRFormatError, DefinitionException, IOException { +// for (Extension ext : element.getModifierExtension()) { +// if (canRender(ext)) { +// String lbl = getExtensionLabel(ext); +// XhtmlNode li = ul.li(); +// li = li.b(); +// li.tx(lbl); +// li.tx(": "); +// render(li, ext.getValue()); +// } else { +// // somehow have to do better than this +// XhtmlNode li = ul.li(); +// li.b().tx(context.formatPhrase(RenderingContext.DATA_REND_UNRD_EX)); +// } +// } +// for (Extension ext : element.getExtension()) { +// if (canRender(ext)) { +// String lbl = getExtensionLabel(ext); +// XhtmlNode li = ul.li(); +// li.tx(lbl); +// li.tx(": "); +// render(li, ext.getValue()); +// } +// } +// } +// + public void renderExtensionsInText(RenderingStatus status, XhtmlNode div, ResourceElement element, String sep) throws FHIRFormatError, DefinitionException, IOException { boolean first = true; - for (Extension ext : element.getExtension()) { + for (ResourceElement ext : element.extensions()) { if (canRender(ext)) { if (first) { first = false; @@ -497,106 +546,139 @@ public class DataRenderer extends Renderer implements CodeResolver { String lbl = getExtensionLabel(ext); div.tx(lbl); div.tx(": "); - render(div, ext.getValue()); + renderDataType(status, div, ext.child("value")); } } } - public void renderExtensionsInList(XhtmlNode div, BackboneType element, String sep) throws FHIRFormatError, DefinitionException, IOException { - boolean first = true; - for (Extension ext : element.getModifierExtension()) { - if (first) { - first = false; - } else { - div.tx(sep); - div.tx(" "); - } - if (canRender(ext)) { - String lbl = getExtensionLabel(ext); - XhtmlNode b = div.b(); - b.tx(lbl); - b.tx(": "); - render(div, ext.getValue()); - } else { - // somehow have to do better than this - div.b().tx(context.formatPhrase(RenderingContext.DATA_REND_UNRD_EX)); - } - } - for (Extension ext : element.getExtension()) { - if (canRender(ext)) { - if (first) { - first = false; - } else { - div.tx(sep); - div.tx(" "); - } - - String lbl = getExtensionLabel(ext); - div.tx(lbl); - div.tx(": "); - render(div, ext.getValue()); - } - } - - } +// public void renderExtensionsInList(XhtmlNode div, BackboneType element, String sep) throws FHIRFormatError, DefinitionException, IOException { +// boolean first = true; +// for (Extension ext : element.getModifierExtension()) { +// if (first) { +// first = false; +// } else { +// div.tx(sep); +// div.tx(" "); +// } +// if (canRender(ext)) { +// String lbl = getExtensionLabel(ext); +// XhtmlNode b = div.b(); +// b.tx(lbl); +// b.tx(": "); +// render(div, ext.getValue()); +// } else { +// // somehow have to do better than this +// div.b().tx(context.formatPhrase(RenderingContext.DATA_REND_UNRD_EX)); +// } +// } +// for (Extension ext : element.getExtension()) { +// if (canRender(ext)) { +// if (first) { +// first = false; +// } else { +// div.tx(sep); +// div.tx(" "); +// } +// +// String lbl = getExtensionLabel(ext); +// div.tx(lbl); +// div.tx(": "); +// render(div, ext.getValue()); +// } +// } +// +// } // -- 6. Data type Rendering ---------------------------------------------- public static String display(IWorkerContext context, DataType type) { - return new DataRenderer(new RenderingContext(context, null, null, "http://hl7.org/fhir/R4", "", null, ResourceRendererMode.END_USER, GenerationRules.VALID_RESOURCE)).display(type); + return new DataRenderer(new RenderingContext(context, null, null, "http://hl7.org/fhir/R4", "", null, ResourceRendererMode.END_USER, GenerationRules.VALID_RESOURCE)).displayDataType(type); } public String displayBase(Base b) { if (b instanceof DataType) { - return display((DataType) b); + return displayDataType((DataType) b); } else { return (context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, b.fhirType()) + " "); } } - public String display(DataType type) { + public String displayDataType(DataType type) { + return displayDataType(new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), type)); + } + + public String displayDataType(ResourceElement type) { if (type == null || type.isEmpty()) { return ""; } - if (type instanceof Coding) { - return displayCoding((Coding) type); - } else if (type instanceof CodeableConcept) { - return displayCodeableConcept((CodeableConcept) type); - } else if (type instanceof Identifier) { - return displayIdentifier((Identifier) type); - } else if (type instanceof HumanName) { - return displayHumanName((HumanName) type); - } else if (type instanceof Address) { - return displayAddress((Address) type); - } else if (type instanceof ContactPoint) { - return displayContactPoint((ContactPoint) type); - } else if (type instanceof Quantity) { - return displayQuantity((Quantity) type); - } else if (type instanceof Range) { - return displayRange((Range) type); - } else if (type instanceof Period) { - return displayPeriod((Period) type); - } else if (type instanceof Timing) { - return displayTiming((Timing) type); - } else if (type instanceof SampledData) { - return displaySampledData((SampledData) type); - } else if (type instanceof ContactDetail) { - return displayContactDetail((ContactDetail) type); - } else if (type.isDateTime()) { - return displayDateTime((BaseDateTimeType) type); - } else if (type.isPrimitive()) { - return context.getTranslated((PrimitiveType) type); - } else { - return (context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, type.fhirType()) + " "); - } + switch (type.fhirType()) { + case "Coding": return displayCoding(type); + case "CodeableConcept": return displayCodeableConcept(type); + case "CodeableReference": return displayCodeableReference(type); + case "Identifier": return displayIdentifier(type); + case "HumanName": return displayHumanName(type); + case "Address": return displayAddress(type); + case "ContactPoint": return displayContactPoint(type); + case "Quantity": return displayQuantity(type); + case "Range": return displayRange(type); + case "Period": return displayPeriod(type); + case "Timing": return displayTiming(type); + case "SampledData": return displaySampledData(type); + case "ContactDetail": return displayContactDetail(type); + case "Annotation": return displayAnnotation(type); + case "Ratio": return displayRatio(type); + case "Reference" : return displayReference(type); + case "dateTime": + case "date" : + case "instant" : + return displayDateTime(type); + default: + if (type.isPrimitive()) { + return context.getTranslated(type); + } else if (Utilities.existsInList(type.fhirType(), "Meta", "Dosage", "Signature", "UsageContext", "RelatedArtifact", "ElementDefinition", "Base64BinaryType", "Attachment")) { + return ""; + } else { + return (context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, type.fhirType()) + " "); + } + } } - protected String displayDateTime(BaseDateTimeType type) { + private String displayAnnotation(ResourceElement type) { + return type.primitiveValue("text"); + } + + private String displayCodeableReference(ResourceElement type) { + if (type.has("reference")) { + return displayReference(type.child("reference")); + } else { + return displayCodeableConcept(type.child("concept")); + } + } + + + private String displayReference(ResourceElement type) { + if (type.has("display")) { + return type.primitiveValue("display"); + } else if (type.has("reference")) { +// ResourceWithReference tr = resolveReference(res, r.getReference()); +// x.addText(tr == null ? r.getReference() : "?ngen-3"); // getDisplayForReference(tr.getReference())); + return "?ngen-3"; + } else { + return "?ngen-4?"; + } + } + + private String displayRatio(ResourceElement type) { + return displayQuantity(type.child("numerator"))+" / "+displayQuantity(type.child("denominator")); + } + + protected String displayDateTime(ResourceElement type) { if (!type.hasPrimitiveValue()) { return ""; } + BaseDateTimeType t = new DateTimeType(type.primitiveValue()); // relevant inputs in rendering context: // timeZone, dateTimeFormat, locale, mode // timezone - application specified timezone to use. @@ -605,10 +687,10 @@ public class DataRenderer extends Renderer implements CodeResolver { // null = default to ... depends on mode // mode - if rendering mode is technical, format defaults to XML format // locale - otherwise, format defaults to SHORT for the Locale (which defaults to default Locale) - if (isOnlyDate(type.getPrecision())) { + if (isOnlyDate(t.getPrecision())) { - DateTimeFormatter fmt = getDateFormatForPrecision(type); - LocalDate date = LocalDate.of(type.getYear(), type.getMonth()+1, type.getDay()); + DateTimeFormatter fmt = getDateFormatForPrecision(t); + LocalDate date = LocalDate.of(t.getYear(), t.getMonth()+1, t.getDay()); return fmt.format(date); } @@ -620,7 +702,7 @@ public class DataRenderer extends Renderer implements CodeResolver { fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withLocale(context.getLocale()); } } - ZonedDateTime zdt = ZonedDateTime.parse(type.primitiveValue()); + ZonedDateTime zdt = ZonedDateTime.parse(t.primitiveValue()); ZoneId zone = context.getTimeZoneId(); if (zone != null) { zdt = zdt.withZoneSameInstant(zone); @@ -669,143 +751,243 @@ public class DataRenderer extends Renderer implements CodeResolver { return temporalPrecisionEnum == TemporalPrecisionEnum.YEAR || temporalPrecisionEnum == TemporalPrecisionEnum.MONTH || temporalPrecisionEnum == TemporalPrecisionEnum.DAY; } - public String display(BaseWrapper type) { - return (context.formatPhrase(RenderingContext.DATA_REND_TO_DO)); - } - public void render(XhtmlNode x, BaseWrapper type) throws FHIRFormatError, DefinitionException, IOException { - Base base = null; - try { - base = type.getBase(); - } catch (FHIRException | IOException e) { - x.tx(context.formatPhrase(RenderingContext.DATA_REND_ERROR, e.getMessage()) + " "); // this shouldn't happen - it's an error in the library itself - return; - } - if (base instanceof DataType) { - render(x, (DataType) base); - } else { - x.tx(context.formatPhrase(RenderingContext.DATA_REND_TO_DO, base.fhirType())); - } - } +// public void renderDataType(RenderingStatus status, XhtmlNode x, ResourceElement type) throws FHIRFormatError, DefinitionException, IOException { +//// Base base = null; +//// try { +//// base = type.getBase(); +//// } catch (FHIRException | IOException e) { +//// x.tx(context.formatPhrase(RenderingContext.DATA_REND_ERROR, e.getMessage()) + " "); // this shouldn't happen - it's an error in the library itself +//// return; +//// } +//// if (base instanceof DataType) { +//// render(x, (DataType) base); +//// } else { +//// x.tx(context.formatPhrase(RenderingContext.DATA_REND_TO_DO, base.fhirType())); +//// } +// } - public void renderBase(XhtmlNode x, Base b) throws FHIRFormatError, DefinitionException, IOException { + public void renderBase(RenderingStatus status, XhtmlNode x, Base b) throws FHIRFormatError, DefinitionException, IOException { if (b instanceof DataType) { - render(x, (DataType) b); + renderDataType(status, x, new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), (DataType) b)); } else { x.tx(context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, b.fhirType()) + " "); } } - public void render(XhtmlNode x, DataType type) throws FHIRFormatError, DefinitionException, IOException { - if (type instanceof BaseDateTimeType) { - x.tx(displayDateTime((BaseDateTimeType) type)); - } else if (type instanceof UriType) { - renderUri(x, (UriType) type); - } else if (type instanceof Annotation) { - renderAnnotation(x, (Annotation) type); - } else if (type instanceof Coding) { - renderCodingWithDetails(x, (Coding) type); - } else if (type instanceof CodeableConcept) { - renderCodeableConcept(x, (CodeableConcept) type); - } else if (type instanceof Identifier) { - renderIdentifier(x, (Identifier) type); - } else if (type instanceof HumanName) { - renderHumanName(x, (HumanName) type); - } else if (type instanceof Address) { - renderAddress(x, (Address) type); - } else if (type instanceof Expression) { - renderExpression(x, (Expression) type); - } else if (type instanceof Money) { - renderMoney(x, (Money) type); - } else if (type instanceof ContactPoint) { - renderContactPoint(x, (ContactPoint) type); - } else if (type instanceof Quantity) { - renderQuantity(x, (Quantity) type); - } else if (type instanceof Range) { - renderRange(x, (Range) type); - } else if (type instanceof Period) { - renderPeriod(x, (Period) type); - } else if (type instanceof Timing) { - renderTiming(x, (Timing) type); - } else if (type instanceof SampledData) { - renderSampledData(x, (SampledData) type); - } else if (type instanceof Reference) { - renderReference(x, (Reference) type); - } else if (type instanceof UsageContext) { - renderUsageContext(x, (UsageContext) type); - } else if (type instanceof CodeableReference) { - CodeableReference cr = (CodeableReference) type; - if (cr.hasConcept()) { - renderCodeableConcept(x, cr.getConcept()); + public boolean renderDataType(RenderingStatus status, XhtmlNode x, ResourceElement type) throws FHIRFormatError, DefinitionException, IOException { + switch (type.fhirType()) { + case "dateTime": + case "date" : + case "instant" : + renderDateTime(status, x, type); + break; + case "uri" : + renderUri(status, x, type); + break; + case "Annotation": + renderAnnotation(status, x, type); + break; + case "Coding": + renderCodingWithDetails(status, x, type); + break; + case "CodeableConcept": + renderCodeableConcept(status, x, type); + break; + case "Identifier": + renderIdentifier(status, x, type); + break; + case "HumanName": + renderHumanName(status, x, type); + break; + case "Address": + renderAddress(status, x, type); + break; + case "Expression": + renderExpression(status, x, type); + break; + case "Money": + renderMoney(status, x, type); + break; + case "ContactPoint": + renderContactPoint(status, x, type); + break; + case "Quantity": + renderQuantity(status, x, type); + break; + case "Range": + renderRange(status, x, type); + break; + case "Period": + renderPeriod(status, x, type); + break; + case "Timing": + renderTiming(status, x, type); + break; + case "SampledData": + renderSampledData(status, x, type); + break; + case "Reference": + renderReference(status, x, type); + break; + case "UsageContext": + renderUsageContext(status, x, type); + break; + case "ContactDetail": + renderContactDetail(status, x, type); + break; + case "Ratio": + renderRatio(status, x, type); + break; + case "CodeableReference": + if (type.has("concept")) { + renderCodeableConcept(status, x, type.child("concept")); } else { - renderReference(x, cr.getReference()); + renderReference(status, x, type.child("reference")); } - } else if (type instanceof MarkdownType) { - addMarkdown(x, context.getTranslated((MarkdownType) type)); - } else if (type instanceof Base64BinaryType) { - Base64BinaryType b64 = (Base64BinaryType) type; - x.tx(context.formatPhrase(RenderingContext.DATA_REND_BASE64, (b64.getValue() == null ? "0" : b64.getValue().length))); - } else if (type.isPrimitive()) { - x.tx(context.getTranslated((PrimitiveType) type)); - } else { - x.tx(context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, type.fhirType()) + " "); + break; + case "MarkdownType": + addMarkdown(x, context.getTranslated(type)); + break; + case "Base64BinaryType": + x.tx(context.formatPhrase(RenderingContext.DATA_REND_BASE64, type.primitiveValue().length())); + default: + if (type.isPrimitive()) { + if (!renderPrimitiveWithNoValue(status, x, type)) { + x.tx(context.getTranslated(type)); + } + } else { + x.tx(context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, type.fhirType()) + " "); + return false; + } } + return true; } - protected void renderReference(XhtmlNode x, Reference ref) { - if (ref.hasDisplay()) { - x.tx(context.getTranslated(ref.getDisplayElement())); - } else if (ref.hasReference()) { - x.tx(ref.getReference()); + private void renderRatio(RenderingStatus status, XhtmlNode x, ResourceElement type) { + renderQuantity(status, x, type.child("numerator")); + x.tx("/"); + renderQuantity(status, x, type.child("denominator")); + } + + private void renderContactDetail(RenderingStatus status, XhtmlNode x, ResourceElement cd) { + if (cd.has("name")) { + x.tx(cd.primitiveValue("name")+": "); + } + boolean first = true; + for (ResourceElement c : cd.children("telecom")) { + if (first) first = false; else x.tx(","); + renderContactPoint(status, x, c); + } + } + + private void renderDateTime(RenderingStatus status, XhtmlNode x, ResourceElement type) throws FHIRFormatError, DefinitionException, IOException { + if (!renderPrimitiveWithNoValue(status, x, type)) { + x.tx(displayDateTime(type)); + } + } + + protected void renderReference(RenderingStatus status, XhtmlNode x, ResourceElement ref) { + if (ref.has("display")) { + x.tx(context.getTranslated(ref.child("display"))); + } else if (ref.has("reference")) { + x.tx(ref.primitiveValue("reference")); } else { x.tx("??"); } } +// +// public void renderDateTime(RenderingStatus status, XhtmlNode x, Base e) { +// if (e.hasPrimitiveValue()) { +// x.addText(displayDateTime((DateTimeType) e)); +// } +// } +// +// public void renderDate(RenderingStatus status, XhtmlNode x, Base e) { +// if (e.hasPrimitiveValue()) { +// x.addText(displayDateTime((DateType) e)); +// } +// } +// +// public void renderDateTime(XhtmlNode x, String s) { +// if (s != null) { +// DateTimeType dt = new DateTimeType(s); +// x.addText(displayDateTime(dt)); +// } +// } - public void renderDateTime(XhtmlNode x, Base e) { - if (e.hasPrimitiveValue()) { - x.addText(displayDateTime((DateTimeType) e)); - } - } - - public void renderDate(XhtmlNode x, Base e) { - if (e.hasPrimitiveValue()) { - x.addText(displayDateTime((DateType) e)); - } - } - - public void renderDateTime(XhtmlNode x, String s) { - if (s != null) { - DateTimeType dt = new DateTimeType(s); - x.addText(displayDateTime(dt)); - } - } - - protected void renderUri(XhtmlNode x, UriType uri) { - if (uri.getValue().startsWith("mailto:")) { - x.ah(uri.getValue()).addText(uri.getValue().substring(7)); - } else { - Resource r = context.getContext().fetchResource(Resource.class, uri.getValue()); - if (r != null && r.getWebPath() != null) { - if (r instanceof CanonicalResource) { - x.ah(r.getWebPath()).addText(crPresent((CanonicalResource) r)); - } else { - x.ah(r.getWebPath()).addText(uri.getValue()); - } + + private boolean renderPrimitiveWithNoValue(RenderingStatus status, XhtmlNode x, ResourceElement prim) throws FHIRFormatError, DefinitionException, IOException { + if (prim.hasPrimitiveValue()) { + return false; + } + boolean first = true; + for (ResourceElement ext : prim.extensions()) { + if (first) first = false; else x.tx(", "); + String url = ext.primitiveValue("url"); + if (url.equals(ToolingExtensions.EXT_DAR)) { + x.tx("Absent because : "); + displayCode(x, ext.child("value")); + } else if (url.equals(ToolingExtensions.EXT_NF)) { + x.tx("Null because: "); + displayCode(x, ext.child("value")); + } else if (url.equals(ToolingExtensions.EXT_OT)) { + x.code().tx("Text: "); + displayCode(x, ext.child("value")); + } else if (url.equals(ToolingExtensions.EXT_CQF_EXP)) { + x.code().tx("Value calculated by: "); + renderExpression(status, x, ext.child("value")); + } else { + StructureDefinition def = context.getContext().fetchResource(StructureDefinition.class, url); + if (def == null) { + x.code().tx(tail(url)+": "); + } else { + x.code().tx(def.present()+": "); + } + renderDataType(status, x, ext.child("value")); + } + } + status.setExtensions(true); + return true; + } + + private String tail(String url) { + return url.contains("/") ? url.substring(url.lastIndexOf("/")+1) : url; +} + + private void displayCode(XhtmlNode x, ResourceElement code) { + x.tx(code.primitiveValue()); + } + + protected void renderUri(RenderingStatus status, XhtmlNode x, ResourceElement uri) throws FHIRFormatError, DefinitionException, IOException { + if (!renderPrimitiveWithNoValue(status, x, uri)) { + String v = uri.primitiveValue(); + if (v.startsWith("mailto:")) { + x.ah(v).addText(v.substring(7)); } else { - String url = context.getResolver() != null ? context.getResolver().resolveUri(context, uri.getValue()) : null; - if (url != null) { - x.ah(url).addText(uri.getValue()); - } else if (Utilities.isAbsoluteUrlLinkable(uri.getValue()) && !(uri instanceof IdType)) { - x.ah(uri.getValue()).addText(uri.getValue()); + Resource r = context.getContext().fetchResource(Resource.class, v); + if (r != null && r.getWebPath() != null) { + if (r instanceof CanonicalResource) { + x.ah(r.getWebPath()).addText(crPresent((CanonicalResource) r)); + } else { + x.ah(r.getWebPath()).addText(v); + } } else { - x.addText(uri.getValue()); + String url = context.getResolver() != null ? context.getResolver().resolveUri(context, v) : null; + if (url != null) { + x.ah(url).addText(v); + } else if (Utilities.isAbsoluteUrlLinkable(v) && !uri.fhirType().equals("id")) { + x.ah(v).addText(v); + } else { + x.addText(v); + } } } - } + } } - protected void renderUri(XhtmlNode x, UriType uriD, String path, String id, Resource src) { + + protected void renderUri(RenderingStatus status, XhtmlNode x, UriType uriD, String path, String id, Resource src) { String uri = uriD.getValue(); if (isCanonical(path)) { x.code().tx(uri); @@ -835,37 +1017,38 @@ public class DataRenderer extends Renderer implements CodeResolver { } } - protected void renderAnnotation(XhtmlNode x, Annotation annot) { - renderAnnotation(x, annot, false); + protected void renderAnnotation(RenderingStatus status, XhtmlNode x, ResourceElement annot) { + renderAnnotation(status, x, annot, false); } - protected void renderAnnotation(XhtmlNode x, Annotation a, boolean showCodeDetails) throws FHIRException { + protected void renderAnnotation(RenderingStatus status, XhtmlNode x, ResourceElement a, boolean showCodeDetails) throws FHIRException { StringBuilder b = new StringBuilder(); - if (a.hasText()) { - b.append(context.getTranslated(a.getTextElement())); + if (a.has("text")) { + b.append(context.getTranslated(a.child("text"))); } - if (a.hasText() && (a.hasAuthor() || a.hasTimeElement())) { + if (a.has("text") && (a.has("author") || a.has("time"))) { b.append(" ("); } - if (a.hasAuthor()) { + if (a.has("author")) { b.append(context.formatPhrase(RenderingContext.DATA_REND_BY) + " "); - if (a.hasAuthorReference()) { - b.append(a.getAuthorReference().getReference()); - } else if (a.hasAuthorStringType()) { - b.append(context.getTranslated(a.getAuthorStringType())); + ResourceElement auth = a.child("author"); + if (auth.fhirType().equals("Reference")) { + b.append(auth.primitiveValue("reference")); + } else if (auth.fhirType().equals("string")) { + b.append(context.getTranslated(auth)); } } - if (a.hasTimeElement()) { + if (a.has("time")) { if (b.length() > 0) { b.append(" "); } - b.append("@").append(a.getTimeElement().toHumanDisplay()); + b.append("@").append(displayDateTime(a.child("time"))); } - if (a.hasText() && (a.hasAuthor() || a.hasTimeElement())) { + if (a.has("text") && (a.has("author") || a.has("time"))) { b.append(")"); } @@ -873,27 +1056,27 @@ public class DataRenderer extends Renderer implements CodeResolver { x.addText(b.toString()); } - public String displayCoding(Coding c) { + public String displayCoding(ResourceElement c) { String s = ""; if (context.isTechnicalMode()) { - s = context.getTranslated(c.getDisplayElement()); + s = context.getTranslated(c.child("display")); if (Utilities.noString(s)) { - s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + s = lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); } if (Utilities.noString(s)) { - s = displayCodeTriple(c.getSystem(), c.getVersion(), c.getCode()); - } else if (c.hasSystem()) { - s = s + " ("+displayCodeTriple(c.getSystem(), c.getVersion(), c.getCode())+")"; - } else if (c.hasCode()) { - s = s + " ("+c.getCode()+")"; + s = displayCodeTriple(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); + } else if (c.has("system")) { + s = s + " ("+displayCodeTriple(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code"))+")"; + } else if (c.has("code")) { + s = s + " ("+c.primitiveValue("code")+")"; } } else { - if (c.hasDisplayElement()) - return context.getTranslated(c.getDisplayElement()); + if (c.has("display")) + return context.getTranslated(c.child("display")); if (Utilities.noString(s)) - s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + s = lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); if (Utilities.noString(s)) - s = c.getCode(); + s = c.primitiveValue("code"); } return s; } @@ -925,13 +1108,13 @@ public class DataRenderer extends Renderer implements CodeResolver { public String displayCoding(List list) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); for (Coding c : list) { - b.append(displayCoding(c)); + b.append(displayCoding(new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), c))); } return b.toString(); } - protected void renderCoding(XhtmlNode x, Coding c) { - renderCoding(x, c, false); + protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceElement c) { + renderCoding(status, x, c, false); } protected void renderCoding(HierarchicalTableGenerator gen, List pieces, Coding c) { @@ -1018,30 +1201,34 @@ public class DataRenderer extends Renderer implements CodeResolver { return resolveCode(new Coding().setSystem(system).setCode(code)); } - public CodeResolution resolveCode(Coding c) { + public CodeResolution resolveCode(ResourceElement c) { String systemName; String systemLink; String link; String display = null; String hint; - if (c.hasDisplayElement()) - display = context.getTranslated(c.getDisplayElement()); + if (c.has("display")) + display = context.getTranslated(c.child("display")); if (Utilities.noString(display)) - display = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + display = lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); if (Utilities.noString(display)) { - display = c.getCode(); + display = c.primitiveValue("code"); } - CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem()); + CodeSystem cs = context.getWorker().fetchCodeSystem(c.primitiveValue("system")); systemLink = cs != null ? cs.getWebPath() : null; - systemName = cs != null ? crPresent(cs) : displaySystem(c.getSystem()); - link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode()); + systemName = cs != null ? crPresent(cs) : displaySystem(c.primitiveValue("system")); + link = getLinkForCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); - hint = systemName+": "+display+(c.hasVersion() ? " "+ context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.getVersion(), ")") : ""); + hint = systemName+": "+display+(c.has("version") ? " "+ context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.primitiveValue("version"), ")") : ""); return new CodeResolution(systemName, systemLink, link, display, hint); } + public CodeResolution resolveCode(Coding code) { + return resolveCode(new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), code)); + } + public CodeResolution resolveCode(CodeableConcept code) { if (code.hasCoding()) { return resolveCode(code.getCodingFirstRep()); @@ -1049,17 +1236,17 @@ public class DataRenderer extends Renderer implements CodeResolver { return new CodeResolution(null, null, null, code.getText(), code.getText()); } } - protected void renderCodingWithDetails(XhtmlNode x, Coding c) { + protected void renderCodingWithDetails(RenderingStatus status, XhtmlNode x, ResourceElement c) { String s = ""; - if (c.hasDisplayElement()) - s = context.getTranslated(c.getDisplayElement()); + if (c.has("display")) + s = context.getTranslated(c.child("display")); if (Utilities.noString(s)) - s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + s = lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); - CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem()); + CodeSystem cs = context.getWorker().fetchCodeSystem(c.primitiveValue("system")); - String sn = cs != null ? crPresent(cs) : displaySystem(c.getSystem()); - String link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode()); + String sn = cs != null ? crPresent(cs) : displaySystem(c.primitiveValue("system")); + String link = getLinkForCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); if (link != null) { x.ah(link).tx(sn); } else { @@ -1067,47 +1254,47 @@ public class DataRenderer extends Renderer implements CodeResolver { } x.tx(" "); - x.tx(c.getCode()); + x.tx(c.primitiveValue("code")); if (!Utilities.noString(s)) { x.tx(": "); x.tx(s); } - if (c.hasVersion()) { - x.tx(" "+context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.getVersion(), ")")); + if (c.has("version")) { + x.tx(" "+context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.primitiveValue("version"), ")")); } } - protected void renderCoding(XhtmlNode x, Coding c, boolean showCodeDetails) { + protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceElement c, boolean showCodeDetails) { String s = ""; - if (c.hasDisplayElement()) - s = context.getTranslated(c.getDisplayElement()); + if (c.has("display")) + s = context.getTranslated(c.child("display")); if (Utilities.noString(s)) - s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + s = lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); if (Utilities.noString(s)) - s = c.getCode(); + s = c.primitiveValue("code"); if (showCodeDetails) { - x.addText(s+" "+context.formatPhrase(RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.getSystem()), c.getCode(), " = '", lookupCode(c.getSystem(), c.getVersion(), c.getCode()), c.getDisplay(), "')")); + x.addText(s+" "+context.formatPhrase(RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.primitiveValue("system")), c.primitiveValue("code"), " = '", lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")), c.primitiveValue("display"), "')")); } else - x.span(null, "{"+c.getSystem()+" "+c.getCode()+"}").addText(s); + x.span(null, "{"+c.primitiveValue("system")+" "+c.primitiveValue("code")+"}").addText(s); } - public String displayCodeableConcept(CodeableConcept cc) { - String s = context.getTranslated(cc.getTextElement()); + public String displayCodeableConcept(ResourceElement cc) { + String s = context.getTranslated(cc.child("Text")); if (Utilities.noString(s)) { - for (Coding c : cc.getCoding()) { - if (c.hasDisplayElement()) { - s = context.getTranslated(c.getDisplayElement()); + for (ResourceElement c : cc.children("coding")) { + if (c.has("display")) { + s = context.getTranslated(c.child("display")); break; } } } if (Utilities.noString(s)) { // still? ok, let's try looking it up - for (Coding c : cc.getCoding()) { - if (c.hasCode() && c.hasSystem()) { - s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + for (ResourceElement c : cc.children("coding")) { + if (c.has("code") && c.has("system")) { + s = lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); if (!Utilities.noString(s)) break; } @@ -1115,46 +1302,46 @@ public class DataRenderer extends Renderer implements CodeResolver { } if (Utilities.noString(s)) { - if (cc.getCoding().isEmpty()) + if (!cc.has("coding")) s = ""; else - s = cc.getCoding().get(0).getCode(); + s = cc.children("coding").get(0).primitiveValue("code"); } return s; } - protected void renderCodeableConcept(XhtmlNode x, CodeableConcept cc) throws FHIRFormatError, DefinitionException, IOException { - renderCodeableConcept(x, cc, false); + protected void renderCodeableConcept(RenderingStatus status, XhtmlNode x, ResourceElement cc) throws FHIRFormatError, DefinitionException, IOException { + renderCodeableConcept(status, x, cc, false); } - protected void renderCodeableReference(XhtmlNode x, CodeableReference e, boolean showCodeDetails) throws FHIRFormatError, DefinitionException, IOException { - if (e.hasConcept()) { - renderCodeableConcept(x, e.getConcept(), showCodeDetails); + protected void renderCodeableReference(RenderingStatus status, XhtmlNode x, ResourceElement e, boolean showCodeDetails) throws FHIRFormatError, DefinitionException, IOException { + if (e.has("concept")) { + renderCodeableConcept(status, x, e.child("concept"), showCodeDetails); } - if (e.hasReference()) { - renderReference(x, e.getReference()); + if (e.has("reference")) { + renderReference(status, x, e.child("reference")); } } - protected void renderCodeableConcept(XhtmlNode x, CodeableConcept cc, boolean showCodeDetails) throws FHIRFormatError, DefinitionException, IOException { + protected void renderCodeableConcept(RenderingStatus status, XhtmlNode x, ResourceElement cc, boolean showCodeDetails) throws FHIRFormatError, DefinitionException, IOException { if (cc.isEmpty()) { return; } - String s = context.getTranslated(cc.getTextElement()); + String s = context.getTranslated(cc.child("text")); if (Utilities.noString(s)) { - for (Coding c : cc.getCoding()) { - if (c.hasDisplayElement()) { - s = context.getTranslated(c.getDisplayElement()); + for (ResourceElement c : cc.children("coding")) { + if (c.has("display")) { + s = context.getTranslated(c.child("display")); break; } } } if (Utilities.noString(s)) { // still? ok, let's try looking it up - for (Coding c : cc.getCoding()) { - if (c.hasCodeElement() && c.hasSystemElement()) { - s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); + for (ResourceElement c : cc.children("coding")) { + if (c.has("code") && c.has("system")) { + s = lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")); if (!Utilities.noString(s)) break; } @@ -1162,10 +1349,10 @@ public class DataRenderer extends Renderer implements CodeResolver { } if (Utilities.noString(s)) { - if (cc.getCoding().isEmpty()) + if (!cc.has("coding")) s = ""; else - s = cc.getCoding().get(0).getCode(); + s = cc.children("coding").get(0).primitiveValue("code"); } if (showCodeDetails) { @@ -1173,38 +1360,38 @@ public class DataRenderer extends Renderer implements CodeResolver { XhtmlNode sp = x.span("background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki", null); sp.tx(" ("); boolean first = true; - for (Coding c : cc.getCoding()) { + for (ResourceElement c : cc.children("coding")) { if (first) { first = false; } else { sp.tx("; "); } - String url = getLinkForSystem(c.getSystem(), c.getVersion()); + String url = getLinkForSystem(c.primitiveValue("system"), c.primitiveValue("version")); if (url != null) { - sp.ah(url).tx(displayCodeSource(c.getSystem(), c.getVersion())); + sp.ah(url).tx(displayCodeSource(c.primitiveValue("system"), c.primitiveValue("version"))); } else { - sp.tx(displayCodeSource(c.getSystem(), c.getVersion())); + sp.tx(displayCodeSource(c.primitiveValue("system"), c.primitiveValue("version"))); } - if (c.hasCode()) { - sp.tx("#"+c.getCode()); + if (c.has("code")) { + sp.tx("#"+c.primitiveValue("code")); } - if (c.hasDisplay() && !s.equals(c.getDisplay())) { - sp.tx(" \""+context.getTranslated(c.getDisplayElement())+"\""); + if (c.has("display") && !s.equals(c.primitiveValue("display"))) { + sp.tx(" \""+context.getTranslated(c.child("display"))+"\""); } } if (hasRenderableExtensions(cc)) { if (!first) { sp.tx("; "); } - renderExtensionsInText(sp, cc, ";"); + renderExtensionsInText(status, sp, cc, ";"); } sp.tx(")"); } else { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (Coding c : cc.getCoding()) { - if (c.hasCodeElement() && c.hasSystemElement()) { - b.append("{"+c.getSystem()+" "+c.getCode()+"}"); + for (ResourceElement c : cc.children("coding")) { + if (c.has("code") && c.has("system")) { + b.append("{"+c.primitiveValue("system")+" "+c.primitiveValue("code")+"}"); } } @@ -1212,57 +1399,59 @@ public class DataRenderer extends Renderer implements CodeResolver { } } - protected String displayIdentifier(Identifier ii) { - String s = Utilities.noString(ii.getValue()) ? "?ngen-9?" : ii.getValue(); - if ("urn:ietf:rfc:3986".equals(ii.getSystem()) && s.startsWith("urn:oid:")) { + protected String displayIdentifier(ResourceElement ii) { + String s = Utilities.noString(ii.primitiveValue("value")) ? "?ngen-9?" : ii.primitiveValue("value"); + if ("urn:ietf:rfc:3986".equals(ii.primitiveValue("system")) && s.startsWith("urn:oid:")) { s = "OID:"+s.substring(8); - } else if ("urn:ietf:rfc:3986".equals(ii.getSystem()) && s.startsWith("urn:uuid:")) { + } else if ("urn:ietf:rfc:3986".equals(ii.primitiveValue("system")) && s.startsWith("urn:uuid:")) { s = "UUID:"+s.substring(9); } else { - NamingSystem ns = context.getContext().getNSUrlMap().get(ii.getSystem()); + NamingSystem ns = context.getContext().getNSUrlMap().get(ii.primitiveValue("system")); if (ns != null) { s = crPresent(ns)+"#"+s; } - if (ii.hasType()) { - if (ii.getType().hasText()) - s = context.getTranslated(ii.getType().getTextElement())+":\u00A0"+s; - else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasDisplay()) - s = context.getTranslated(ii.getType().getCoding().get(0).getDisplayElement())+": "+s; - else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasCode()) - s = lookupCode(ii.getType().getCoding().get(0).getSystem(), ii.getType().getCoding().get(0).getVersion(), ii.getType().getCoding().get(0).getCode())+": "+s; - } else if (ii.hasSystem()) { - s = ii.getSystem()+"#"+s; + if (ii.has("type")) { + ResourceElement type = ii.child("type"); + if (type.has("text")) + s = context.getTranslated(type.child("text"))+":\u00A0"+s; + else if (type.has("coding") && type.children("coding").get(0).has("display")) + s = context.getTranslated(type.children("coding").get(0).child("display"))+": "+s; + else if (type.has("coding") && type.children("coding").get(0).has("code")) + s = lookupCode(type.children("coding").get(0).primitiveValue("system"), type.children("coding").get(0).primitiveValue("version"), type.children("coding").get(0).primitiveValue("code")); + } else if (ii.has("system")) { + s = ii.primitiveValue("system")+"#"+s; } } - if (ii.hasUse() || ii.hasPeriod()) { + if (ii.has("use") || ii.has("period")) { s = s + "\u00A0("; - if (ii.hasUse()) { - s = s + "use:\u00A0"+ii.getUse().toCode(); + if (ii.has("use")) { + s = s + "use:\u00A0"+ii.primitiveValue("use"); } - if (ii.hasUse() && ii.hasPeriod()) { + if (ii.has("use") || ii.has("period")) { s = s + ",\u00A0"; } - if (ii.hasPeriod()) { - s = s + "period:\u00A0"+displayPeriod(ii.getPeriod()); + if (ii.has("period")) { + s = s + "period:\u00A0"+displayPeriod(ii.child("period")); } s = s + ")"; } return s; } - protected void renderIdentifier(XhtmlNode x, Identifier ii) { - if (ii.hasType()) { - if (ii.getType().hasText()) { - x.tx(context.getTranslated(ii.getType().getTextElement())); - } else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasDisplay()) { - x.tx(context.getTranslated(ii.getType().getCoding().get(0).getDisplayElement())); - } else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasCode()) { - x.tx(lookupCode(ii.getType().getCoding().get(0).getSystem(), ii.getType().getCoding().get(0).getVersion(), ii.getType().getCoding().get(0).getCode())); + protected void renderIdentifier(RenderingStatus status, XhtmlNode x, ResourceElement ii) { + if (ii.has("type")) { + ResourceElement type = ii.child("type"); + if (type.has("text")) { + x.tx(context.getTranslated(type.child("text"))); + } else if (type.has("coding") && type.children("coding").get(0).has("display")) { + x.tx(context.getTranslated(type.children("coding").get(0).child("display"))); + } else if (type.has("coding") && type.children("coding").get(0).has("code")) { + x.tx(lookupCode(type.children("coding").get(0).primitiveValue("system"), type.children("coding").get(0).primitiveValue("version"), type.children("coding").get(0).primitiveValue("code"))); } x.tx("/"); - } else if (ii.hasSystem()) { - NamingSystem ns = context.getContext().getNSUrlMap().get(ii.getSystem()); + } else if (ii.has("system")) { + NamingSystem ns = context.getContext().getNSUrlMap().get(ii.primitiveValue("system")); if (ns != null) { if (ns.hasWebPath()) { x.ah(ns.getWebPath(), ns.getDescription()).tx(crPresent(ns)); @@ -1270,135 +1459,136 @@ public class DataRenderer extends Renderer implements CodeResolver { x.tx(crPresent(ns)); } } else { - switch (ii.getSystem()) { + switch (ii.primitiveValue("system")) { case "urn:oid:2.51.1.3": x.ah("https://www.gs1.org/standards/id-keys/gln", context.formatPhrase(RenderingContext.DATA_REND_GLN)).tx("GLN"); break; default: - x.code(ii.getSystem()); + x.code(ii.primitiveValue("system")); } } x.tx("/"); } - x.tx(Utilities.noString(ii.getValue()) ? "?ngen-9?" : ii.getValue()); + x.tx(Utilities.noString(ii.primitiveValue("value")) ? "?ngen-9?" : ii.primitiveValue("value")); - if (ii.hasUse() || ii.hasPeriod()) { + if (ii.has("use") || ii.has("period")) { x.nbsp(); x.tx("("); - if (ii.hasUse()) { + if (ii.has("use")) { x.tx(context.formatPhrase(RenderingContext.DATA_REND_USE)); x.nbsp(); - x.tx(ii.getUse().toCode()); + x.tx(ii.primitiveValue("use")); } - if (ii.hasUse() && ii.hasPeriod()) { + if (ii.has("use") || ii.has("period")) { x.tx(","); x.nbsp(); } - if (ii.hasPeriod()) { + if (ii.has("period")) { x.tx(context.formatPhrase(RenderingContext.DATA_REND_PERIOD)); x.nbsp(); - x.tx(displayPeriod(ii.getPeriod())); + x.tx(displayPeriod(ii.child("period"))); } x.tx(")"); } } - public static String displayHumanName(HumanName name) { + public static String displayHumanName(ResourceElement name) { StringBuilder s = new StringBuilder(); - if (name.hasText()) - s.append(name.getText()); + if (name.has("text")) + s.append(name.primitiveValue("text")); else { - for (StringType p : name.getGiven()) { - s.append(p.getValue()); + for (ResourceElement p : name.children("given")) { + s.append(p.primitiveValue()); s.append(" "); } - if (name.hasFamily()) { - s.append(name.getFamily()); + if (name.has("family")) { + s.append(name.primitiveValue("family")); s.append(" "); } } - if (name.hasUse() && name.getUse() != NameUse.USUAL) - s.append("("+name.getUse().toString()+")"); + if (name.has("use") && !name.primitiveValue("use").equals("usual")) + s.append("("+name.primitiveValue("use")+")"); return s.toString(); } - protected void renderHumanName(XhtmlNode x, HumanName name) { + protected void renderHumanName(RenderingStatus status, XhtmlNode x, ResourceElement name) { StringBuilder s = new StringBuilder(); - if (name.hasText()) - s.append(context.getTranslated(name.getTextElement())); + if (name.has("text")) + s.append(context.getTranslated(name.child("text"))); else { - for (StringType p : name.getGiven()) { + for (ResourceElement p : name.children("given")) { s.append(context.getTranslated(p)); s.append(" "); } - if (name.hasFamily()) { - s.append(context.getTranslated(name.getFamilyElement())); + if (name.has("family")) { + s.append(context.getTranslated(name.child("family"))); s.append(" "); } } - if (name.hasUse() && name.getUse() != NameUse.USUAL) - s.append("("+context.getTranslatedCode(name.getUseElement(), "http://hl7.org/fhir/name-use")+")"); - + if (name.has("use") && !name.primitiveValue("use").equals("usual")) { + s.append("("+context.getTranslatedCode(name.primitiveValue("use"), "http://hl7.org/fhir/name-use")+")"); + } x.addText(s.toString()); } - private String displayAddress(Address address) { + private String displayAddress(ResourceElement address) { StringBuilder s = new StringBuilder(); - if (address.hasText()) - s.append(context.getTranslated(address.getTextElement())); + if (address.has("text")) + s.append(context.getTranslated(address.child("text"))); else { - for (StringType p : address.getLine()) { + for (ResourceElement p : address.children("line")) { s.append(context.getTranslated(p)); s.append(" "); } - if (address.hasCity()) { - s.append(context.getTranslated(address.getCityElement())); + if (address.has("city")) { + s.append(context.getTranslated(address.child("city"))); s.append(" "); } - if (address.hasState()) { - s.append(context.getTranslated(address.getStateElement())); + if (address.has("state")) { + s.append(context.getTranslated(address.child("state"))); s.append(" "); } - if (address.hasPostalCode()) { - s.append(context.getTranslated(address.getPostalCodeElement())); + if (address.has("postalCode")) { + s.append(context.getTranslated(address.child("postalCode"))); s.append(" "); } - if (address.hasCountry()) { - s.append(context.getTranslated(address.getCountryElement())); + if (address.has("country")) { + s.append(context.getTranslated(address.child("country"))); s.append(" "); } } - if (address.hasUse()) - s.append("("+address.getUse().toCode()+")"); + if (address.has("use")) { + s.append("("+address.primitiveValue("use")+")"); + } return s.toString(); } - protected void renderAddress(XhtmlNode x, Address address) { + protected void renderAddress(RenderingStatus status, XhtmlNode x, ResourceElement address) { x.addText(displayAddress(address)); } - public static String displayContactPoint(ContactPoint contact) { + public static String displayContactPoint(ResourceElement contact) { StringBuilder s = new StringBuilder(); - s.append(describeSystem(contact.getSystem())); - if (Utilities.noString(contact.getValue())) + s.append(describeSystem(contact.primitiveValue("system"))); + if (Utilities.noString(contact.primitiveValue("value"))) s.append("-unknown-"); else - s.append(contact.getValue()); - if (contact.hasUse()) - s.append("("+contact.getUse().toString()+")"); + s.append(contact.primitiveValue("value")); + if (contact.has("use")) + s.append("("+contact.primitiveValue("use")+")"); return s.toString(); } - public static String displayContactDetail(ContactDetail contact) { + public static String displayContactDetail(ResourceElement contact) { CommaSeparatedStringBuilder s = new CommaSeparatedStringBuilder(); - for (ContactPoint cp : contact.getTelecom()) { + for (ResourceElement cp : contact.children("telecom")) { s.append(displayContactPoint(cp)); } - return contact.getName()+(s.length() == 0 ? "" : " ("+s.toString()+")"); + return contact.primitiveValue("name")+(s.length() == 0 ? "" : " ("+s.toString()+")"); } protected String getLocalizedBigDecimalValue(BigDecimal input, Currency c) { @@ -1409,51 +1599,51 @@ public class DataRenderer extends Renderer implements CodeResolver { return numberFormat.format(input); } - protected void renderMoney(XhtmlNode x, Money money) { + protected void renderMoney(RenderingStatus status, XhtmlNode x, ResourceElement money) { if (x.getName().equals("blockquote")) { x = x.para(); } - Currency c = money.hasCurrency() ? Currency.getInstance(money.getCurrency()) : null; + Currency c = money.has("currency") ? Currency.getInstance(money.primitiveValue("currency")) : null; if (c != null) { XhtmlNode s = x.span(null, c.getDisplayName()); s.tx(c.getSymbol(context.getLocale())); - s.tx(getLocalizedBigDecimalValue(money.getValue(), c)); + s.tx(getLocalizedBigDecimalValue(new BigDecimal(money.primitiveValue("value")), c)); x.tx(" ("+c.getCurrencyCode()+")"); } else { - if (money.getCurrency() != null) { - x.tx(money.getCurrency()); + if (money.has("currency")) { + x.tx(money.primitiveValue("currency")); } - x.tx(money.getValue().toPlainString()); + x.tx(money.primitiveValue("value")); } } - protected void renderExpression(XhtmlNode x, Expression expr) { + protected void renderExpression(RenderingStatus status, XhtmlNode x, ResourceElement expr) { // there's two parts: what the expression is, and how it's described. // we start with what it is, and then how it's described XhtmlNode p = x; if (p.getName().equals("blockquote")) { p = p.para(); } - if (expr.hasExpression()) { - if (expr.hasReference()) { - p = x.ah(expr.getReference()); + if (expr.has("expression")) { + if (expr.has("reference")) { + p = x.ah(expr.primitiveValue("reference")); } XhtmlNode c = p; - if (expr.hasLanguage()) { - c = c.span(null, expr.getLanguage()); + if (expr.has("language")) { + c = c.span(null, expr.primitiveValue("language")); } - c.code().tx(expr.getExpression()); - } else if (expr.hasReference()) { - p.ah(expr.getReference()).tx(context.formatPhrase(RenderingContext.DATA_REND_SOURCE)); + c.code().tx(expr.primitiveValue("expression")); + } else if (expr.has("reference")) { + p.ah(expr.primitiveValue("reference")).tx(context.formatPhrase(RenderingContext.DATA_REND_SOURCE)); } - if (expr.hasName() || expr.hasDescription()) { + if (expr.has("name") || expr.has("description")) { p.tx("("); - if (expr.hasName()) { - p.b().tx(expr.getName()); + if (expr.has("name")) { + p.b().tx(expr.primitiveValue("name")); } - if (expr.hasDescription()) { + if (expr.has("description")) { p.tx("\""); - p.tx(context.getTranslated(expr.getDescriptionElement())); + p.tx(context.getTranslated(expr.child("description"))); p.tx("\""); } p.tx(")"); @@ -1461,39 +1651,37 @@ public class DataRenderer extends Renderer implements CodeResolver { } - protected void renderContactPoint(XhtmlNode x, ContactPoint contact) { + protected void renderContactPoint(RenderingStatus status, XhtmlNode x, ResourceElement contact) { if (contact != null) { - if (!contact.hasSystem()) { + if (!contact.has("system")) { x.addText(displayContactPoint(contact)); } else { - switch (contact.getSystem()) { - case EMAIL: - x.ah("mailto:"+contact.getValue()).tx(contact.getValue()); + String v = contact.primitiveValue("value"); + switch (contact.primitiveValue("system")) { + case "email": + x.ah("mailto:"+v).tx(v); break; - case FAX: + case "fax": x.addText(displayContactPoint(contact)); break; - case NULL: + case "other": x.addText(displayContactPoint(contact)); break; - case OTHER: + case "pager": x.addText(displayContactPoint(contact)); break; - case PAGER: - x.addText(displayContactPoint(contact)); - break; - case PHONE: - if (contact.hasValue() && contact.getValue() != null && contact.getValue().startsWith("+")) { - x.ah("tel:"+contact.getValue().replace(" ", "")).tx(contact.getValue()); + case "phone": + if (contact.has("value") && v != null && v.startsWith("+")) { + x.ah("tel:"+v.replace(" ", "")).tx(v); } else { x.addText(displayContactPoint(contact)); } break; - case SMS: + case "sms": x.addText(displayContactPoint(contact)); break; - case URL: - x.ah(contact.getValue()).tx(contact.getValue()); + case "url": + x.ah(v).tx(v); break; default: break; @@ -1534,274 +1722,276 @@ public class DataRenderer extends Renderer implements CodeResolver { p.ah(c.getValue()).addText(c.getValue()); } } - private static String describeSystem(ContactPointSystem system) { + private static String describeSystem(String system) { if (system == null) return ""; switch (system) { - case PHONE: return "ph: "; - case FAX: return "fax: "; + case "phone": return "ph: "; + case "fax": return "fax: "; default: return ""; } } - protected String displayQuantity(Quantity q) { + protected String displayQuantity(ResourceElement q) { StringBuilder s = new StringBuilder(); - s.append(q.hasValue() ? q.getValue() : "?"); - if (q.hasUnit()) - s.append(" ").append(q.getUnit()); - else if (q.hasCode()) - s.append(" ").append(q.getCode()); + s.append(q.has("value") ? q.primitiveValue("value") : "?"); + if (q.has("unit")) + s.append(" ").append(q.primitiveValue("unit")); + else if (q.has("code")) + s.append(" ").append(q.primitiveValue("code")); return s.toString(); } - protected void renderQuantity(XhtmlNode x, Quantity q) { - renderQuantity(x, q, false); + protected void renderQuantity(RenderingStatus status, XhtmlNode x, ResourceElement q) { + renderQuantity(status, x, q, false); } - protected void renderQuantity(XhtmlNode x, Quantity q, boolean showCodeDetails) { - if (q.hasComparator()) - x.addText(q.getComparator().toCode()); - if (q.hasValue()) { - x.addText(context.getTranslated(q.getValueElement())); + protected void renderQuantity(RenderingStatus status, XhtmlNode x, ResourceElement q, boolean showCodeDetails) { + if (q.has("comparator")) + x.addText(q.primitiveValue("comparator")); + if (q.has("value")) { + x.addText(context.getTranslated(q.child("value"))); } - if (q.hasUnit()) - x.tx(" "+context.getTranslated(q.getUnitElement())); - else if (q.hasCode() && q.hasSystem()) { + if (q.has("unit")) + x.tx(" "+context.getTranslated(q.child("unit"))); + else if (q.has("code") && q.has("system")) { // if there's a code there *shall* be a system, so if we've got one and not the other, things are invalid and we won't bother trying to render - if (q.hasSystem() && q.getSystem().equals("http://unitsofmeasure.org")) - x.tx(" "+q.getCode()); + if (q.has("system") && q.primitiveValue("system").equals("http://unitsofmeasure.org")) + x.tx(" "+q.primitiveValue("code")); else - x.tx("(unit "+q.getCode()+" from "+q.getSystem()+")"); + x.tx("(unit "+q.primitiveValue("code")+" from "+q.primitiveValue("system")+")"); } - if (showCodeDetails && q.hasCode()) { - x.span("background: LightGoldenRodYellow", null).tx(" "+ (context.formatPhrase(RenderingContext.DATA_REND_DETAILS, displaySystem(q.getSystem()))) +q.getCode()+" = '"+lookupCode(q.getSystem(), null, q.getCode())+"')"); + if (showCodeDetails && q.has("code")) { + x.span("background: LightGoldenRodYellow", null).tx(" "+ (context.formatPhrase(RenderingContext.DATA_REND_DETAILS, displaySystem(q.primitiveValue("system")))) +q.primitiveValue("code")+" = '"+lookupCode(q.primitiveValue("system"), null, q.primitiveValue("code"))+"')"); } } protected void renderQuantity(HierarchicalTableGenerator gen, List pieces, Quantity q, boolean showCodeDetails) { - pieces.add(gen.new Piece(null, displayQuantity(q), null)); + pieces.add(gen.new Piece(null, displayQuantity(new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), q)), null)); } - public String displayRange(Range q) { - if (!q.hasLow() && !q.hasHigh()) + public String displayRange(ResourceElement q) { + if (!q.has("low") && !q.has("high")) return "?"; StringBuilder b = new StringBuilder(); - boolean sameUnits = (q.getLow().hasUnit() && q.getHigh().hasUnit() && q.getLow().getUnit().equals(q.getHigh().getUnit())) - || (q.getLow().hasCode() && q.getHigh().hasCode() && q.getLow().getCode().equals(q.getHigh().getCode())); + boolean sameUnits = (q.child("low").has("unit") && q.child("high").has("unit") && q.child("low").child("unit").matches(q.child("high").child("unit"))) + || (q.child("low").has("code") && q.child("high").has("code") && q.child("low").child("code").matches(q.child("high").child("code"))); String low = "?"; - if (q.hasLow() && q.getLow().hasValue()) - low = sameUnits ? q.getLow().getValue().toString() : displayQuantity(q.getLow()); - String high = displayQuantity(q.getHigh()); + if (q.has("low") && q.child("low").has("value")) + low = sameUnits ? q.child("low").primitiveValue("value").toString() : displayQuantity(q.child("low")); + String high = displayQuantity(q.child("high")); if (high.isEmpty()) high = "?"; b.append(low).append("\u00A0to\u00A0").append(high); return b.toString(); } - protected void renderRange(XhtmlNode x, Range q) { - if (q.hasLow()) - x.addText(q.getLow().getValue().toString()); + protected void renderRange(RenderingStatus status, XhtmlNode x, ResourceElement q) { + if (q.has("low")) + x.addText(q.child("low").primitiveValue("value").toString()); else x.tx("?"); x.tx("-"); - if (q.hasHigh()) - x.addText(q.getHigh().getValue().toString()); + if (q.has("high")) + x.addText(q.child("high").primitiveValue("value").toString()); else x.tx("?"); - if (q.getLow().hasUnit()) - x.tx(" "+q.getLow().getUnit()); + if (q.child("low").has("unit")) + x.tx(" "+q.child("low").child("unit")); } - public String displayPeriod(Period p) { - String s = !p.hasStart() ? "(?)" : displayDateTime(p.getStartElement()); + public String displayPeriod(ResourceElement p) { + String s = !p.has("start") ? "(?)" : displayDateTime(p.child("start")); s = s + " --> "; - return s + (!p.hasEnd() ? context.formatPhrase(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.getEndElement())); + return s + (!p.has("end") ? context.formatPhrase(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.child("end"))); } - public void renderPeriod(XhtmlNode x, Period p) { - x.addText(!p.hasStart() ? "??" : displayDateTime(p.getStartElement())); + public void renderPeriod(RenderingStatus status, XhtmlNode x, ResourceElement p) { + x.addText(!p.has("start") ? "??" : displayDateTime(p.child("start"))); x.tx(" --> "); - x.addText(!p.hasEnd() ? context.formatPhrase(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.getEndElement())); + x.addText(!p.has("end") ? context.formatPhrase(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.child("end"))); } - public void renderUsageContext(XhtmlNode x, UsageContext u) throws FHIRFormatError, DefinitionException, IOException { - renderCoding(x, u.getCode()); + public void renderUsageContext(RenderingStatus status, XhtmlNode x, ResourceElement u) throws FHIRFormatError, DefinitionException, IOException { + renderCoding(status, x, u.child("code")); x.tx(": "); - render(x, u.getValue()); + renderDataType(status, x, u.child("value")); } - public void renderTriggerDefinition(XhtmlNode x, TriggerDefinition td) throws FHIRFormatError, DefinitionException, IOException { + public void renderTriggerDefinition(RenderingStatus status, XhtmlNode x, ResourceElement td) throws FHIRFormatError, DefinitionException, IOException { if (x.isPara()) { x.b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); x.tx(": "); - x.tx(td.getType().getDisplay()); + x.tx(td.child("type").primitiveValue("display")); - if (td.hasName()) { + if (td.has("name")) { x.tx(", "); x.b().tx(context.formatPhrase(RenderingContext.GENERAL_NAME)); x.tx(": "); - x.tx(context.getTranslated(td.getNameElement())); + x.tx(context.getTranslated(td.child("name"))); } - if (td.hasCode()) { + if (td.has("code")) { x.tx(", "); x.b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); x.tx(": "); - renderCodeableConcept(x, td.getCode()); + renderCodeableConcept(status, x, td.child("code")); } - if (td.hasTiming()) { + if (td.has("timing")) { x.tx(", "); x.b().tx(context.formatPhrase(RenderingContext.DATA_REND_TIMING)); x.tx(": "); - render(x, td.getTiming()); + renderDataType(status, x, td.child("timing")); } - if (td.hasCondition()) { + if (td.has("condition")) { x.tx(", "); x.b().tx(context.formatPhrase(RenderingContext.DATA_REND_COND)); x.tx(": "); - renderExpression(x, td.getCondition()); + renderExpression(status, x, td.child("condition")); } } else { XhtmlNode tbl = x.table("grid"); XhtmlNode tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); - tr.td().tx(td.getType().getDisplay()); + tr.td().tx(td.child("type").primitiveValue("display")); - if (td.hasName()) { + if (td.has("name")) { tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_NAME)); - tr.td().tx(context.getTranslated(td.getNameElement())); + tr.td().tx(context.getTranslated(td.child("name"))); } - if (td.hasCode()) { + if (td.has("code")) { tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); - renderCodeableConcept(tr.td(), td.getCode()); + renderCodeableConcept(status, tr.td(), td.child("code")); } - if (td.hasTiming()) { + if (td.has("timing")) { tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.DATA_REND_TIMING)); - render(tr.td(), td.getTiming()); + renderDataType(status, tr.td(), td.child("timing")); } - if (td.hasCondition()) { + if (td.has("condition")) { tr = tbl.tr(); tr.td().b().tx(context.formatPhrase(RenderingContext.DATA_REND_COND)); - renderExpression(tr.td(), td.getCondition()); + renderExpression(status, tr.td(), td.child("condition")); } } } - public void renderDataRequirement(XhtmlNode x, DataRequirement dr) throws FHIRFormatError, DefinitionException, IOException { + public void renderDataRequirement(RenderingStatus status, XhtmlNode x, ResourceElement dr) throws FHIRFormatError, DefinitionException, IOException { XhtmlNode tbl = x.table("grid"); XhtmlNode tr = tbl.tr(); XhtmlNode td = tr.td().colspan("2"); td.b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); td.tx(": "); - StructureDefinition sd = context.getWorker().fetchTypeDefinition(dr.getType().toCode()); + StructureDefinition sd = context.getWorker().fetchTypeDefinition(dr.primitiveValue("type")); if (sd != null && sd.hasWebPath()) { - td.ah(sd.getWebPath()).tx(dr.getType().toCode()); + td.ah(sd.getWebPath()).tx(dr.primitiveValue("type")); } else { - td.tx(dr.getType().toCode()); + td.tx(dr.primitiveValue("type")); } - if (dr.hasProfile()) { + if (dr.has("profile")) { td.tx(" ("); boolean first = true; - for (CanonicalType p : dr.getProfile()) { + for (ResourceElement p : dr.children("profile")) { if (first) first = false; else td.tx(" | "); - sd = context.getWorker().fetchResource(StructureDefinition.class, p.getValue()); + sd = context.getWorker().fetchResource(StructureDefinition.class, p.primitiveValue()); if (sd != null && sd.hasWebPath()) { td.ah(sd.getWebPath()).tx(crPresent(sd)); } else { - td.tx(p.asStringValue()); + td.tx(p.primitiveValue()); } } td.tx(")"); } - if (dr.hasSubject()) { + if (dr.has("subject")) { tr = tbl.tr(); td = tr.td().colspan("2"); td.b().tx(context.formatPhrase(RenderingContext.GENERAL_SUBJ)); - if (dr.hasSubjectReference()) { - renderReference(td, dr.getSubjectReference()); + ResourceElement subj = dr.child("subject"); + if (subj.fhirType().equals("reference")) { + renderReference(status, td, subj); } else { - renderCodeableConcept(td, dr.getSubjectCodeableConcept()); + renderCodeableConcept(status, td, subj); } } - if (dr.hasCodeFilter() || dr.hasDateFilter()) { + if (dr.has("codeFilter") || dr.has("dateFilter")) { tr = tbl.tr().backgroundColor("#efefef"); tr.td().tx(context.formatPhrase(RenderingContext.GENERAL_FILTER)); tr.td().tx(context.formatPhrase(RenderingContext.GENERAL_VALUE)); } - for (DataRequirementCodeFilterComponent cf : dr.getCodeFilter()) { + for (ResourceElement cf : dr.children("codeFilter")) { tr = tbl.tr(); - if (cf.hasPath()) { - tr.td().tx(cf.getPath()); + if (cf.has("path")) { + tr.td().tx(cf.primitiveValue("path")); } else { - tr.td().tx(context.formatPhrase(RenderingContext.DATA_REND_SEARCH, cf.getSearchParam()) + " "); + tr.td().tx(context.formatPhrase(RenderingContext.DATA_REND_SEARCH, cf.primitiveValue("searchParam")) + " "); } - if (cf.hasValueSet()) { + if (cf.has("valueSet")) { td = tr.td(); td.tx(context.formatPhrase(RenderingContext.DATA_REND_VALUESET) + " "); - render(td, cf.getValueSetElement()); + renderDataType(status, td, cf.child("valueSet")); } else { boolean first = true; td = tr.td(); td.tx(context.formatPhrase(RenderingContext.DATA_REND_THESE_CODES) + " "); - for (Coding c : cf.getCode()) { + for (ResourceElement c : cf.children("code")) { if (first) first = false; else td.tx(", "); - render(td, c); + renderDataType(status, td, c); } } } - for (DataRequirementDateFilterComponent cf : dr.getDateFilter()) { + for (ResourceElement cf : dr.children("dateFilter")) { tr = tbl.tr(); - if (cf.hasPath()) { - tr.td().tx(cf.getPath()); + if (cf.has("path")) { + tr.td().tx(cf.primitiveValue("path")); } else { - tr.td().tx(context.formatPhrase(RenderingContext.DATA_REND_SEARCH, cf.getSearchParam()) + " "); + tr.td().tx(context.formatPhrase(RenderingContext.DATA_REND_SEARCH, cf.primitiveValue("searchParam")) + " "); } - render(tr.td(), cf.getValue()); + renderDataType(status, tr.td(), cf.child("value")); } - if (dr.hasSort() || dr.hasLimit()) { + if (dr.has("sort") || dr.has("limit")) { tr = tbl.tr(); td = tr.td().colspan("2"); - if (dr.hasLimit()) { + if (dr.has("limit")) { td.b().tx(context.formatPhrase(RenderingContext.DATA_REND_LIMIT)); td.tx(": "); - td.tx(dr.getLimit()); - if (dr.hasSort()) { + td.tx(dr.primitiveValue("limit")); + if (dr.has("sort")) { td.tx(", "); } } - if (dr.hasSort()) { + if (dr.has("sort")) { td.b().tx(context.formatPhrase(RenderingContext.DATA_REND_SORT)); td.tx(": "); boolean first = true; - for (DataRequirementSortComponent p : dr.getSort()) { + for (ResourceElement p : dr.children("sort")) { if (first) first = false; else td.tx(" | "); - td.tx(p.getDirection() == SortDirection.ASCENDING ? "+" : "-"); - td.tx(p.getPath()); + td.tx(p.primitiveValue("direction").equals("ascending") ? "+" : "-"); + td.tx(p.primitiveValue("path")); } } } } - private String displayTiming(Timing s) throws FHIRException { + private String displayTiming(ResourceElement s) throws FHIRException { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - if (s.hasCode()) - b.append(context.formatPhrase(RenderingContext.GENERAL_CODE, displayCodeableConcept(s.getCode())) + " "); - - if (s.getEvent().size() > 0) { + if (s.has("code")) { + b.append(context.formatPhrase(RenderingContext.GENERAL_CODE, displayCodeableConcept(s.child("code"))) + " "); + } + + if (s.has("event")) { CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); - for (DateTimeType p : s.getEvent()) { - if (p.hasValue()) { + for (ResourceElement p : s.children("event")) { + if (p.hasPrimitiveValue()) { c.append(displayDateTime(p)); } else if (!renderExpression(c, p)) { c.append("??"); @@ -1810,126 +2000,127 @@ public class DataRenderer extends Renderer implements CodeResolver { b.append(context.formatPhrase(RenderingContext.DATA_REND_EVENTS, c.toString()) + " "); } - if (s.hasRepeat()) { - TimingRepeatComponent rep = s.getRepeat(); - if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasStart()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_STARTING, displayDateTime(rep.getBoundsPeriod().getStartElement())) + " "); - if (rep.hasCount()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_COUNT, Integer.toString(rep.getCount())) + " " + " times"); - if (rep.hasDuration()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_DURATION, rep.getDuration().toPlainString()+displayTimeUnits(rep.getPeriodUnit())) + " "); + if (s.has("repeat")) { + ResourceElement rep = s.child("repeat"); + if (rep.has("boundsPeriod") && rep.child("boundsPeriod").has("start")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_STARTING, displayDateTime(rep.child("boundsPeriod").child("start"))) + " "); + if (rep.has("count")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_COUNT, rep.primitiveValue("count")) + " " + " times"); + if (rep.has("duration")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_DURATION, rep.primitiveValue("duration")+displayTimeUnits(rep.primitiveValue("periodUnit"))) + " "); - if (rep.hasWhen()) { + if (rep.has("when")) { String st = ""; - if (rep.hasOffset()) { - st = Integer.toString(rep.getOffset())+"min "; + if (rep.has("offset")) { + st = rep.primitiveValue("offset")+"min "; } b.append(st); - for (Enumeration wh : rep.getWhen()) - b.append(displayEventCode(wh.getValue())); + for (ResourceElement wh : rep.children("when")) { + b.append(displayEventCode(wh.primitiveValue())); + } } else { String st = ""; - if (!rep.hasFrequency() || (!rep.hasFrequencyMax() && rep.getFrequency() == 1) ) + if (!rep.has("frequency") || (!rep.has("frequencyMax") && rep.primitiveValue("frequency").equals("1"))) { st = context.formatPhrase(RenderingContext.DATA_REND_ONCE); - else { - st = Integer.toString(rep.getFrequency()); - if (rep.hasFrequencyMax()) - st = st + "-"+Integer.toString(rep.getFrequency()); + } else { + st = rep.primitiveValue("frequency"); + if (rep.has("frequencyMax")) + st = st + "-"+rep.primitiveValue("frequencyMax"); } - if (rep.hasPeriod()) { - st = st + " "+ (context.formatPhrase(RenderingContext.DATA_REND_PER))+rep.getPeriod().toPlainString(); - if (rep.hasPeriodMax()) - st = st + "-"+rep.getPeriodMax().toPlainString(); - st = st + " "+displayTimeUnits(rep.getPeriodUnit()); + if (rep.has("period")) { + st = st + " "+ (context.formatPhrase(RenderingContext.DATA_REND_PER))+rep.primitiveValue("period"); + if (rep.has("periodMax")) + st = st + "-"+rep.primitiveValue("periodMax"); + st = st + " "+displayTimeUnits(rep.primitiveValue("periodUnit")); } b.append(st); } - if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasEnd()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_UNTIL, displayDateTime(rep.getBoundsPeriod().getEndElement())) + " "); + if (rep.has("boundsPeriod") && rep.child("boundsPeriod").has("end")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_UNTIL, displayDateTime(rep.child("boundsPeriod").child("end"))) + " "); } return b.toString(); } - private boolean renderExpression(CommaSeparatedStringBuilder c, PrimitiveType p) { - Extension exp = p.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/cqf-expression"); + private boolean renderExpression(CommaSeparatedStringBuilder c, ResourceElement p) { + ResourceElement exp = p.extensionValue("http://hl7.org/fhir/StructureDefinition/cqf-expression"); if (exp == null) { return false; } - c.append(exp.getValueExpression().getExpression()); + c.append(exp.child("value").primitiveValue("expression")); return true; } - private String displayEventCode(EventTiming when) { + private String displayEventCode(String when) { switch (when) { - case C: return (context.formatPhrase(RenderingContext.DATA_REND_MEALS)); - case CD: return (context.formatPhrase(RenderingContext.DATA_REND_ATLUNCH)); - case CM: return (context.formatPhrase(RenderingContext.DATA_REND_ATBKFST)); - case CV: return (context.formatPhrase(RenderingContext.DATA_REND_ATDINR)); - case AC: return (context.formatPhrase(RenderingContext.DATA_REND_BFMEALS)); - case ACD: return (context.formatPhrase(RenderingContext.DATA_REND_BFLUNCH)); - case ACM: return (context.formatPhrase(RenderingContext.DATA_REND_BFBKFST)); - case ACV: return (context.formatPhrase(RenderingContext.DATA_REND_BFDINR)); - case HS: return (context.formatPhrase(RenderingContext.DATA_REND_BFSLEEP)); - case PC: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRMEALS)); - case PCD: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRLUNCH)); - case PCM: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRBKFST)); - case PCV: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRDINR)); - case WAKE: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRWKNG)); + case "c": return (context.formatPhrase(RenderingContext.DATA_REND_MEALS)); + case "cd": return (context.formatPhrase(RenderingContext.DATA_REND_ATLUNCH)); + case "cm": return (context.formatPhrase(RenderingContext.DATA_REND_ATBKFST)); + case "cv": return (context.formatPhrase(RenderingContext.DATA_REND_ATDINR)); + case "ac": return (context.formatPhrase(RenderingContext.DATA_REND_BFMEALS)); + case "acd": return (context.formatPhrase(RenderingContext.DATA_REND_BFLUNCH)); + case "acm": return (context.formatPhrase(RenderingContext.DATA_REND_BFBKFST)); + case "acv": return (context.formatPhrase(RenderingContext.DATA_REND_BFDINR)); + case "hs": return (context.formatPhrase(RenderingContext.DATA_REND_BFSLEEP)); + case "pc": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRMEALS)); + case "pcd": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRLUNCH)); + case "pcm": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRBKFST)); + case "pcv": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRDINR)); + case "wake": return (context.formatPhrase(RenderingContext.DATA_REND_AFTRWKNG)); default: return "?ngen-6?"; } } - private String displayTimeUnits(UnitsOfTime units) { + private String displayTimeUnits(String units) { if (units == null) return "?ngen-7?"; switch (units) { - case A: return "years"; - case D: return "days"; - case H: return "hours"; - case MIN: return "minutes"; - case MO: return "months"; - case S: return "seconds"; - case WK: return "weeks"; + case "a": return "years"; + case "d": return "days"; + case "h": return "hours"; + case "min": return "minutes"; + case "mo": return "months"; + case "s": return "seconds"; + case "wk": return "weeks"; default: return "?ngen-8?"; } } - protected void renderTiming(XhtmlNode x, Timing s) throws FHIRException { + protected void renderTiming(RenderingStatus status, XhtmlNode x, ResourceElement s) throws FHIRException { x.addText(displayTiming(s)); } - private String displaySampledData(SampledData s) { + private String displaySampledData(ResourceElement s) { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - if (s.hasOrigin()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_ORIGIN, displayQuantity(s.getOrigin())) + " "); + if (s.has("origin")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_ORIGIN, displayQuantity(s.child("origin"))) + " "); - if (s.hasInterval()) { - b.append(context.formatPhrase(RenderingContext.DATA_REND_INT, s.getInterval().toString()) + " "); + if (s.has("interval")) { + b.append(context.formatPhrase(RenderingContext.DATA_REND_INT, s.primitiveValue("interval")) + " "); - if (s.hasIntervalUnit()) - b.append(s.getIntervalUnit().toString()); + if (s.has("intervalUnit")) + b.append(s.primitiveValue("intervalUnit")); } - if (s.hasFactor()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_FACT, s.getFactor().toString()) + " "); + if (s.has("factor")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_FACT, s.primitiveValue("factor")) + " "); - if (s.hasLowerLimit()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_LOWER, s.getLowerLimit().toString()) + " "); + if (s.has("lowerLimit")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_LOWER, s.primitiveValue("lowerLimit")) + " "); - if (s.hasUpperLimit()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_UP, s.getUpperLimit().toString()) + " "); + if (s.has("upperLimit")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_UP, s.primitiveValue("upperLimit")) + " "); - if (s.hasDimensions()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_DIM, s.getDimensions()) + " "); + if (s.has("dimensions")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_DIM, s.primitiveValue("dimensions")) + " "); - if (s.hasData()) - b.append(context.formatPhrase(RenderingContext.DATA_REND_DATA, s.getData()) + " "); + if (s.has("data")) + b.append(context.formatPhrase(RenderingContext.DATA_REND_DATA, s.primitiveValue("data")) + " "); return b.toString(); } - protected void renderSampledData(XhtmlNode x, SampledData sampledData) { + protected void renderSampledData(RenderingStatus status, XhtmlNode x, ResourceElement sampledData) { x.addText(displaySampledData(sampledData)); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java index 5c5d97e8c..dc1a39679 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java @@ -150,6 +150,11 @@ public class DiagnosticReportRenderer extends ResourceRenderer { public boolean render(XhtmlNode x, DiagnosticReport dr) throws IOException, FHIRException, EOperationOutcome { render(x, new DirectWrappers.ResourceWrapperDirect(this.context, dr)); + for (Resource resource : dr.getContained()) { + x.hr(); + RendererFactory.factory(resource, context).render(x, resource); + } + return true; } @@ -461,7 +466,9 @@ public class DiagnosticReportRenderer extends ResourceRenderer { td = tr.td(); pw = getProperty(obs, "note"); if (valued(pw)) { - render(td, pw.value()); + for (BaseWrapper b : pw.getValues()) { + render(td, b); + } } } if (effectiveTime) { 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 6abe12781..7f8d25869 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 @@ -73,7 +73,6 @@ import org.hl7.fhir.r5.renderers.utils.DirectWrappers.PropertyWrapperDirect; import org.hl7.fhir.r5.renderers.utils.DirectWrappers.ResourceWrapperDirect; import org.hl7.fhir.r5.renderers.utils.ElementWrappers; import org.hl7.fhir.r5.renderers.utils.RenderingContext; -import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceWithReference; import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.ToolingExtensions; @@ -286,7 +285,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { // - public void generateResourceSummary(XhtmlNode x, ResourceWrapper res, boolean textAlready, boolean showCodeDetails, boolean canLink) throws FHIRException, UnsupportedEncodingException, IOException { + public void generateResourceSummary(XhtmlNode x, ResourceElement res, boolean textAlready, boolean showCodeDetails, boolean canLink) throws FHIRException, UnsupportedEncodingException, IOException { if (!textAlready) { XhtmlNode div = res.getNarrative(); if (div != null) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java index 4b91febe1..a55326535 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java @@ -37,6 +37,18 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode; */ public class Renderer { + public static class RenderingStatus { + private boolean extensions; + + public void setExtensions(boolean b) { + extensions = b; + } + + public boolean getExtensions() { + return extensions; + } + + } protected RenderingContext context; public Renderer(RenderingContext context) { 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 8991a5cda..fd6189685 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 @@ -4,7 +4,7 @@ import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; import org.hl7.fhir.r5.renderers.utils.RenderingContext; -import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; +import org.hl7.fhir.r5.renderers.utils.ResourceElement; import org.hl7.fhir.utilities.Utilities; public class RendererFactory { @@ -62,14 +62,14 @@ public class RendererFactory { } - public static ResourceRenderer factory(ResourceWrapper resource, RenderingContext context, ResourceContext resourceContext) { + public static ResourceRenderer factory(ResourceElement resource, RenderingContext context) { if (context.getTemplateProvider() != null) { - String liquidTemplate = context.getTemplateProvider().findTemplate(context, resource.getName()); + String liquidTemplate = context.getTemplateProvider().findTemplate(context, resource.fhirType()); if (liquidTemplate != null) { return new LiquidRenderer(context, liquidTemplate); } } - switch (resource.getName()) { + switch (resource.fhirType()) { case "DiagnosticReport": return new DiagnosticReportRenderer(context); case "Library": return new LibraryRenderer(context); case "List": return new ListRenderer(context); @@ -77,11 +77,11 @@ public class RendererFactory { case "QuestionnaireResponse": return new QuestionnaireResponseRenderer(context); } - return new ProfileDrivenRenderer(context, resourceContext); + return new ProfileDrivenRenderer(context); } public static ResourceRenderer factory(ResourceWrapper rw, RenderingContext lrc) { - return factory(rw, lrc, null); + return factory(rw, lrc); } public static boolean hasSpecificRenderer(String rt) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index f35b0c8f0..b2360bf79 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -1,6 +1,7 @@ package org.hl7.fhir.r5.renderers; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.HashMap; import java.util.ArrayList; @@ -24,6 +25,7 @@ import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.Narrative; import org.hl7.fhir.r5.model.PrimitiveType; +import org.hl7.fhir.r5.model.Property; import org.hl7.fhir.r5.model.Narrative.NarrativeStatus; import org.hl7.fhir.r5.model.Reference; import org.hl7.fhir.r5.model.Resource; @@ -34,9 +36,9 @@ import org.hl7.fhir.r5.renderers.utils.DirectWrappers.ResourceWrapperDirect; import org.hl7.fhir.r5.renderers.utils.ElementWrappers.ResourceWrapperMetaElement; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; -import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceReferenceKind; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceWithReference; +import org.hl7.fhir.r5.renderers.utils.ResourceElement; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.ToolingExtensions; @@ -54,7 +56,6 @@ public abstract class ResourceRenderer extends DataRenderer { } - protected ResourceContext rcontext; protected XVerExtensionManager xverManager; protected boolean multiLangMode; @@ -62,21 +63,6 @@ public abstract class ResourceRenderer extends DataRenderer { public ResourceRenderer(RenderingContext context) { super(context); } - - public ResourceRenderer(RenderingContext context, ResourceContext rcontext) { - super(context); - this.rcontext = rcontext; - } - - public ResourceContext getRcontext() { - return rcontext; - } - - public ResourceRenderer setRcontext(ResourceContext rcontext) { - this.rcontext = rcontext; - return this; - } - public boolean isMultiLangMode() { return multiLangMode; @@ -87,13 +73,16 @@ public abstract class ResourceRenderer extends DataRenderer { return this; } - public XhtmlNode build(Resource dr) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome { + public XhtmlNode build(ResourceElement dr) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome { XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); - render(x, dr); + renderResource(new RenderingStatus(), x, dr); return x; } + /** - * given a resource, update it's narrative with the best rendering available + * given a resource, update it's narrative with the best rendering available. + * + * Explanation about native vs wrapped * * @param r - the domain resource in question * @@ -101,39 +90,33 @@ public abstract class ResourceRenderer extends DataRenderer { * @throws EOperationOutcome * @throws FHIRException */ + public void renderResource(DomainResource r) throws IOException, FHIRException, EOperationOutcome { + XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); + RenderingStatus status = new RenderingStatus(); + renderResource(status, x, r); + String an = r.fhirType()+"_"+r.getId(); + if (context.isAddName()) { + if (!hasAnchorName(x, an)) { + injectAnchorName(x, an); + } + } + inject(r, x, status.getExtensions() ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED); + } - public void render(DomainResource r) throws IOException, FHIRException, EOperationOutcome { + public void renderResource(ResourceElement r) throws IOException, FHIRException, EOperationOutcome { XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); - boolean hasExtensions; - hasExtensions = render(x, r); + RenderingStatus status = new RenderingStatus(); + renderResource(status, x, r); String an = r.fhirType()+"_"+r.getId(); if (context.isAddName()) { if (!hasAnchorName(x, an)) { injectAnchorName(x, an); } } - inject(r, x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED); + inject(r, x, status.getExtensions() ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED); } - public XhtmlNode render(ResourceWrapper r) throws IOException, FHIRException, EOperationOutcome { - assert r.getContext() == context; - XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); - boolean hasExtensions = render(x, r); - - String an = r.fhirType()+"_"+r.getId(); - if (context.isAddName()) { - if (!hasAnchorName(x, an)) { - injectAnchorName(x, an); - } - } - if (r.hasNarrative()) { - r.injectNarrative(this, x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED); - } - return x; - } - - public XhtmlNode checkNarrative(ResourceWrapper r) throws IOException, FHIRException, EOperationOutcome { - assert r.getContext() == context; + public XhtmlNode checkNarrative(ResourceElement r) throws IOException, FHIRException, EOperationOutcome { XhtmlNode x = r.getNarrative(); String an = r.fhirType()+"_"+r.getId(); if (context.isAddName()) { @@ -166,24 +149,35 @@ public abstract class ResourceRenderer extends DataRenderer { return false; } - public abstract boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome; - - public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { - ProfileDrivenRenderer pr = new ProfileDrivenRenderer(context); - return pr.render(x, r); + // these three are what the descendants of this class override + public abstract void renderResource(RenderingStatus status, XhtmlNode x, ResourceElement r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome; + public void renderResource(RenderingStatus status, XhtmlNode x, DomainResource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { + renderResource(status, x, new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), r)); + } + public abstract String displayResource(ResourceElement r) throws UnsupportedEncodingException, IOException; + + + public String canonicalTitle(ResourceElement r) { + if (r.has("title")) { + return r.primitiveValue("title"); + } + if (r.has("name")) { + return r.primitiveValue("name"); + } + if (r.has("id")) { + return r.primitiveValue("id"); + } + return "??"; } - public void describe(XhtmlNode x, Resource r) throws UnsupportedEncodingException, IOException { - x.tx(display(r)); + public void describe(XhtmlNode x, ResourceElement r) throws UnsupportedEncodingException, IOException { + x.tx(displayDataType(r)); + } + + public void inject(ResourceElement r, XhtmlNode x, NarrativeStatus status) { + r.setNarrative(x, status.toCode(), multiLangMode, context.getLocale()); } - public void describe(XhtmlNode x, ResourceWrapper r) throws UnsupportedEncodingException, IOException { - x.tx(display(r)); - } - - public abstract String display(Resource r) throws UnsupportedEncodingException, IOException; - public abstract String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException; - public void inject(DomainResource r, XhtmlNode x, NarrativeStatus status) { r.getText().setUserData("renderer.generated", true); if (!r.hasText() || !r.getText().hasDiv()) { @@ -212,20 +206,23 @@ public abstract class ResourceRenderer extends DataRenderer { } } - public void renderCanonical(Resource res, XhtmlNode x, String url) throws UnsupportedEncodingException, IOException { - ResourceWrapper rw = new ResourceWrapperDirect(this.context, res); - renderCanonical(rw, x, url); - } - - public void renderCanonical(ResourceWrapper rw, XhtmlNode x, String url) throws UnsupportedEncodingException, IOException { - renderCanonical(rw, x, url, true, rw.getResource()); + public void markLanguage(XhtmlNode x) { + x.setAttribute("lang", context.getLocale().toString()); + x.setAttribute("xml:lang", context.getLocale().toString()); + x.addTag(0, "hr"); + x.addTag(0, "p").b().tx(context.getLocale().getDisplayName()); + x.addTag(0, "hr"); } - public void renderCanonical(ResourceWrapper rw, XhtmlNode x, String url, boolean allowLinks, Resource src) throws UnsupportedEncodingException, IOException { + public void renderCanonical(ResourceElement res, XhtmlNode x, String url) throws UnsupportedEncodingException, IOException { + renderCanonical(res, x, url, true, res); + } + + public void renderCanonical(ResourceElement rw, XhtmlNode x, String url, boolean allowLinks, ResourceElement src) throws UnsupportedEncodingException, IOException { if (url == null) { return; } - Resource target = context.getWorker().fetchResource(Resource.class, url, src); + Resource target = context.getWorker().fetchResource(Resource.class, url, src.getResource()); if (target == null || !(target instanceof CanonicalResource)) { x.code().tx(url); } else { @@ -249,56 +246,65 @@ public abstract class ResourceRenderer extends DataRenderer { } } - public void render(Resource res, XhtmlNode x, DataType type) throws FHIRFormatError, DefinitionException, IOException { - if (type instanceof Reference) { - renderReference(res, x, (Reference) type); - } else if (type instanceof CodeableReference) { - CodeableReference cr = (CodeableReference) type; - if (cr.hasReference()) { - renderReference(res, x, cr.getReference()); + public void renderReference(RenderingStatus status, ResourceElement res, XhtmlNode x, ResourceElement type) throws FHIRFormatError, DefinitionException, IOException { + if (type.fhirType().equals("Reference")) { + renderReference(status, res, x, type); + } else if (type.fhirType().equals("CodeableReference")) { + if (type.has("reference")) { + renderReference(status, res, x, type, true); } else { - render(x, type); + renderDataType(status, x, type); } } else { - render(x, type); + renderDataType(status, x, type); } } - public void render(ResourceWrapper res, XhtmlNode x, DataType type) throws FHIRFormatError, DefinitionException, IOException { - if (type instanceof Reference) { - renderReference(res, x, (Reference) type); - } else if (type instanceof CodeableReference) { - CodeableReference cr = (CodeableReference) type; - if (cr.hasReference()) { - renderReference(res, x, cr.getReference()); + /* + * } else if (ew.fhirType().equals("Reference")) { + Reference r = (Reference) e; + if (r.getReference() != null && r.getReference().contains("#")) { + if (containedIds.contains(r.getReference().substring(1))) { + x.ah("#hc"+r.getReference().substring(1)).tx("See "+r.getReference()); + } else { + // in this case, we render the resource in line + ResourceWrapper rw = null; + for (ResourceWrapper t : res.getContained()) { + if (r.getReference().substring(1).equals(t.getId())) { + rw = t; + } + } + if (rw == null) { + renderReference(res, x, r); + } else { + String ref = context.getResolver() != null ?context.getResolver().urlForContained(context, res.fhirType(), res.getId(), rw.fhirType(), rw.getId()) : null; + if (ref == null) { + x.an("hc"+rw.getId()); + RenderingContext ctxtc = context.copy(); + ctxtc.setAddGeneratedNarrativeHeader(false); + ctxtc.setContained(true); + ResourceRenderer rr = RendererFactory.factory(rw, ctxtc); + rr.setRcontext(new ResourceContext(rcontext, rw)); + rr.render(parent.blockquote(), rw); + } else { + x.ah(ref).tx("See "+rw.fhirType()); + } + } + } } else { - render(x, type); - } - } else { - render(x, type); - } - } - - public void renderReference(Resource res, XhtmlNode x, Reference r) throws UnsupportedEncodingException, IOException { - ResourceWrapper rw = new ResourceWrapperDirect(this.context, res); - renderReference(rw, x, r); - } - - public void renderReference(ResourceWrapper rw, XhtmlNode x, Reference r) throws UnsupportedEncodingException, IOException { - renderReference(rw, x, r, true); - } - + renderReference(res, x, r); + } + */ public void renderReference(Resource res, HierarchicalTableGenerator gen, List pieces, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException { if (r == null) { pieces.add(gen.new Piece(null, "null!", null)); return; } - ResourceWrapper rw = new ResourceWrapperDirect(this.context, res); ResourceWithReference tr = null; String link = null; StringBuilder text = new StringBuilder(); if (r.hasReferenceElement() && allowLinks) { - tr = resolveReference(rw, r.getReference()); + tr = resolveReference(new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), res), r.getReference()); if (!r.getReference().startsWith("#")) { if (tr != null && tr.getReference() != null) { @@ -315,13 +321,13 @@ public abstract class ResourceRenderer extends DataRenderer { } // what to display: if text is provided, then that. if the reference was resolved, then show the name, or the generated narrative String display = r.hasDisplayElement() ? r.getDisplay() : null; - String name = tr != null && tr.getResource() != null ? tr.getResource().getNameFromResource() : null; + String name = tr != null && tr.getResource() != null ? getNameForResource(tr.getResource()) : null; if (display == null && (tr == null || tr.getResource() == null)) { if (!Utilities.noString(r.getReference())) { text.append(r.getReference()); } else if (r.hasIdentifier()) { - text.append(displayIdentifier(r.getIdentifier())); + text.append(displayIdentifier(new ResourceElement(context.getContextUtilities(), context.getProfileUtilities(), r.getIdentifier()))); } else { text.append("??"); } @@ -364,7 +370,31 @@ public abstract class ResourceRenderer extends DataRenderer { pieces.add(gen.new Piece(link,text.toString(), null)); } - public void renderReference(ResourceWrapper rw, XhtmlNode x, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException { + private String getNameForResource(ResourceElement resource) { + ResourceElement name = resource.firstChild("name"); + if (name != null && !name.isEmpty()) { + if (name.isPrimitive()) { + return name.primitiveValue(); + } else if (name.fhirType().equals("HumanName")) { + String family = name.primitiveValue("family"); + String given = name.firstPrimitiveValue("given"); + return (family == null) ? given : given == null ? family : family+" "+given; + } else { + String n = name.primitiveValueMN("name", "text", "value"); + if (n != null) { + return n; + } + } + } + String n = resource.primitiveValue("productName"); + if (n == null) { + throw new Error("What to render for 'name'? Type is "+resource.fhirType()); + } else { + return n; + } + } + + public void renderReference(RenderingStatus status, ResourceElement rw, XhtmlNode x, ResourceElement r, boolean allowLinks) throws UnsupportedEncodingException, IOException { if (r == null) { x.tx("null!"); return; @@ -372,10 +402,11 @@ public abstract class ResourceRenderer extends DataRenderer { XhtmlNode c = null; ResourceWithReference tr = null; boolean onPage = false; - if (r.hasReferenceElement() && allowLinks) { - tr = resolveReference(rw, r.getReference()); + String rref = r.primitiveValue("reference"); + if (r.has("reference") && allowLinks) { + tr = resolveReference(rw, r.primitiveValue("reference")); - if (!r.getReference().startsWith("#")) { + if (!rref.startsWith("#")) { if (tr != null && tr.getReference() != null) { if (tr.getReference().startsWith("#")) { onPage = true; @@ -387,16 +418,16 @@ public abstract class ResourceRenderer extends DataRenderer { } else { c = x.ah(tr.getReference()); } - } else if (r.getReference().contains("?")) { + } else if (rref.contains("?")) { x.tx(context.formatPhrase(RenderingContext.RES_REND_COND_REF)+" "); c = x.code(""); } else { - c = x.ah(r.getReference()); + c = x.ah(rref); } - } else if ("#".equals(r.getReference())) { + } else if ("#".equals(rref)) { c = x.ah("#"); } else if (context.getRules() == GenerationRules.IG_PUBLISHER || (tr != null && tr.getKind() != ResourceReferenceKind.BUNDLE)) { - c = x.ah("#hc"+r.getReference().substring(1)); + c = x.ah("#hc"+rref.substring(1)); onPage = true; } else { c = x; @@ -408,19 +439,19 @@ public abstract class ResourceRenderer extends DataRenderer { c.tx(context.formatPhrase(RenderingContext.RES_REND_SEE_ON_THIS_PAGE)+" "); } // what to display: if text is provided, then that. if the reference was resolved, then show the name, or the generated narrative - String display = r.hasDisplayElement() ? r.getDisplay() : null; - String name = tr != null && tr.getResource() != null ? tr.getResource().getNameFromResource() : null; + String display = r.has("display") ? r.primitiveValue("display") : null; + String name = tr != null && tr.getResource() != null ? getNameForResource(tr.getResource()) : null; if (display == null && (tr == null || tr.getResource() == null)) { - if (!Utilities.noString(r.getReference())) { - c.addText(r.getReference()); - } else if (r.hasIdentifier()) { - renderIdentifier(c, r.getIdentifier()); + if (!Utilities.noString(rref)) { + c.addText(rref); + } else if (r.has("identifier")) { + renderIdentifier(status, c, r.child("identifier")); } else { c.addText("??"); } } else if (context.isTechnicalMode()) { - c.addText(r.getReference()); + c.addText(rref); if (display != null) { c.addText(": "+display); } @@ -429,16 +460,16 @@ public abstract class ResourceRenderer extends DataRenderer { } if (r.hasExtension(ToolingExtensions.EXT_TARGET_ID) || r.hasExtension(ToolingExtensions.EXT_TARGET_PATH)) { x.addText("("); - for (Extension ex : r.getExtensionsByUrl(ToolingExtensions.EXT_TARGET_ID)) { - if (ex.hasValue()) { + for (ResourceElement ex : r.extensions(ToolingExtensions.EXT_TARGET_ID)) { + if (ex.has("value")) { x.sep(", "); - x.addText("#"+ex.getValue().primitiveValue()); + x.addText("#"+ex.primitiveValue("value")); } } - for (Extension ex : r.getExtensionsByUrl(ToolingExtensions.EXT_TARGET_PATH)) { - if (ex.hasValue()) { + for (ResourceElement ex : r.extensions(ToolingExtensions.EXT_TARGET_PATH)) { + if (ex.has("value")) { x.sep(", "); - x.addText("/#"+ex.getValue().primitiveValue()); + x.addText("/#"+ex.primitiveValue("value")); } } x.addText(")"); @@ -451,48 +482,48 @@ public abstract class ResourceRenderer extends DataRenderer { } else { c.tx(context.formatPhrase(RenderingContext.RES_REND_GEN_SUM)+" "); if (tr != null) { - new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), true, r.getReference().startsWith("#"), true); + new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), true, rref.startsWith("#"), true); } } } } - - public void renderReference(ResourceWrapper rw, XhtmlNode x, BaseWrapper r) throws UnsupportedEncodingException, IOException { - XhtmlNode c = x; - ResourceWithReference tr = null; - String v; - if (r.has("reference")) { - v = r.get("reference").primitiveValue(); - tr = resolveReference(rw, v); - - if (!v.startsWith("#")) { - if (tr != null && tr.getReference() != null) - c = x.ah(tr.getReference()); - else - c = x.ah(v); - } - } else { - v = ""; - } - // what to display: if text is provided, then that. if the reference was resolved, then show the generated narrative - if (r.has("display")) { - c.addText(r.get("display").primitiveValue()); - if (tr != null && tr.getResource() != null) { - c.tx(context.formatPhrase(RenderingContext.RES_REND_GEN_SUM)+" "); - new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), true, v.startsWith("#"), false); - } - } else if (tr != null && tr.getResource() != null) { - new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), v.startsWith("#"), v.startsWith("#"), false); - } else { - c.addText(v); - } - } +// +// public void renderReference(ResourceWrapper rw, XhtmlNode x, BaseWrapper r) throws UnsupportedEncodingException, IOException { +// XhtmlNode c = x; +// ResourceWithReference tr = null; +// String v; +// if (r.has("reference")) { +// v = r.get("reference").primitiveValue(); +// tr = resolveReference(rw, v); +// +// if (!v.startsWith("#")) { +// if (tr != null && tr.getReference() != null) +// c = x.ah(tr.getReference()); +// else +// c = x.ah(v); +// } +// } else { +// v = ""; +// } +// // what to display: if text is provided, then that. if the reference was resolved, then show the generated narrative +// if (r.has("display")) { +// c.addText(r.get("display").primitiveValue()); +// if (tr != null && tr.getResource() != null) { +// c.tx(context.formatPhrase(RenderingContext.RES_REND_GEN_SUM)+" "); +// new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), true, v.startsWith("#"), false); +// } +// } else if (tr != null && tr.getResource() != null) { +// new ProfileDrivenRenderer(context).generateResourceSummary(c, tr.getResource(), v.startsWith("#"), v.startsWith("#"), false); +// } else { +// c.addText(v); +// } +// } - protected ResourceWithReference resolveReference(ResourceWrapper res, String url) { + protected ResourceWithReference resolveReference(ResourceElement res, String url) { if (url == null) return null; if (url.startsWith("#") && res != null) { - for (ResourceWrapper r : res.getContained()) { + for (ResourceElement r : res.children("contained")) { if (r.getId().equals(url.substring(1))) return new ResourceWithReference(ResourceReferenceKind.CONTAINED, url, r); } @@ -503,7 +534,7 @@ public abstract class ResourceRenderer extends DataRenderer { version = url.substring(url.indexOf("/_history/")+10); url = url.substring(0, url.indexOf("/_history/")); } - + /* if (rcontext != null) { BundleEntryComponent bundleResource = rcontext.resolve(url); if (bundleResource != null) { @@ -512,7 +543,7 @@ public abstract class ResourceRenderer extends DataRenderer { id = makeIdFromBundleEntry(bundleResource.getFullUrl()); } String bundleUrl = "#" + bundleResource.getResource().getResourceType().name() + "_" + id; - return new ResourceWithReference(ResourceReferenceKind.BUNDLE, bundleUrl, new ResourceWrapperDirect(this.context, bundleResource.getResource())); + return new ResourceWithReference(ResourceReferenceKind.BUNDLE, bundleUrl, new ResourceElement(this.context.getContextUtilities(), bundleResource.getResource())); } org.hl7.fhir.r5.elementmodel.Element bundleElement = rcontext.resolveElement(url, version); if (bundleElement != null) { @@ -527,13 +558,13 @@ public abstract class ResourceRenderer extends DataRenderer { } else { bundleUrl = "#" +fullUrlToAnchor(bundleElement.getChildValue("fullUrl")); } - return new ResourceWithReference(ResourceReferenceKind.BUNDLE, bundleUrl, new ResourceWrapperMetaElement(this.context, br)); + return new ResourceWithReference(ResourceReferenceKind.BUNDLE, bundleUrl, new ResourceElement(this.context.getContextUtilities(), br)); } } - +*/ Resource ae = getContext().getWorker().fetchResource(null, url, version); if (ae != null) - return new ResourceWithReference(ResourceReferenceKind.EXTERNAL, url, new ResourceWrapperDirect(this.context, ae)); + return new ResourceWithReference(ResourceReferenceKind.EXTERNAL, url, new ResourceElement(this.context.getContextUtilities(), this.context.getProfileUtilities(), ae)); else if (context.getResolver() != null) { return context.getResolver().resolve(context, url); } else @@ -570,14 +601,6 @@ public abstract class ResourceRenderer extends DataRenderer { return null; } - protected PropertyWrapper getProperty(ResourceWrapper res, String name) { - for (PropertyWrapper t : res.children()) { - if (t.getName().equals(name)) - return t; - } - return null; - } - protected PropertyWrapper getProperty(BaseWrapper res, String name) { for (PropertyWrapper t : res.children()) { if (t.getName().equals(name)) @@ -591,18 +614,18 @@ public abstract class ResourceRenderer extends DataRenderer { } - protected ResourceWrapper fetchResource(BaseWrapper subject) throws UnsupportedEncodingException, FHIRException, IOException { - if (context.getResolver() == null) - return null; - - PropertyWrapper ref = subject.getChildByName("reference"); - if (ref == null || !ref.hasValues()) { - return null; - } - String url = ref.value().getBase().primitiveValue(); - ResourceWithReference rr = context.getResolver().resolve(context, url); - return rr == null ? null : rr.getResource(); - } +// protected ResourceElement fetchResource(BaseWrapper subject) throws UnsupportedEncodingException, FHIRException, IOException { +// if (context.getResolver() == null) +// return null; +// +// PropertyWrapper ref = subject.getChildByName("reference"); +// if (ref == null || !ref.hasValues()) { +// return null; +// } +// String url = ref.value().getBase().primitiveValue(); +// ResourceWithReference rr = context.getResolver().resolve(context, url); +// return rr == null ? null : rr.getResource(); +// } protected String describeStatus(PublicationStatus status, boolean experimental) { @@ -637,22 +660,23 @@ public abstract class ResourceRenderer extends DataRenderer { return true; } - protected void renderResourceHeader(ResourceWrapper r, XhtmlNode x, boolean doId) throws UnsupportedEncodingException, FHIRException, IOException { + protected void renderResourceHeader(ResourceElement r, XhtmlNode x, boolean doId) throws UnsupportedEncodingException, FHIRException, IOException { XhtmlNode div = x.div().style("display: inline-block").style("background-color: #d9e0e7").style("padding: 6px") .style("margin: 4px").style("border: 1px solid #8da1b4") .style("border-radius: 5px").style("line-height: 60%"); - - String id = getPrimitiveValue(r, "id"); + + RenderingStatus status = new RenderingStatus(); + String id = r.primitiveValue("id"); if (doId) { div.an("hc"+id); } - String lang = getPrimitiveValue(r, "language"); - String ir = getPrimitiveValue(r, "implicitRules"); - BaseWrapper meta = r.getChildByName("meta").hasValues() ? r.getChildByName("meta").getValues().get(0) : null; - String versionId = getPrimitiveValue(meta, "versionId"); - String lastUpdated = getPrimitiveValue(meta, "lastUpdated"); - String source = getPrimitiveValue(meta, "source"); + String lang = r.primitiveValue("language"); + String ir = r.primitiveValue("implicitRules"); + ResourceElement meta = r.has("meta") && r.child("meta").isEmpty() ? r.child("meta") : null; + ResourceElement versionId = meta == null ? null : meta.child("versionId"); + ResourceElement lastUpdated = meta == null ? null : meta.child("lastUpdated"); + ResourceElement source = meta == null ? null : meta.child("source"); if (id != null || lang != null || versionId != null || lastUpdated != null) { XhtmlNode p = plateStyle(div.para()); @@ -667,7 +691,7 @@ public abstract class ResourceRenderer extends DataRenderer { } if (lastUpdated != null) { p.tx(context.formatPhrase(RenderingContext.RES_REND_UPDATED) + "\""); - renderDateTime(p, lastUpdated); + renderDataType(status, p, lastUpdated); p.tx("\" "); } if (lang != null) { @@ -681,42 +705,34 @@ public abstract class ResourceRenderer extends DataRenderer { plateStyle(div.para()).tx(context.formatPhrase(RenderingContext.RES_REND_INFO_SOURCE) + " "+source+"!"); } if (meta != null) { - PropertyWrapper pl = meta.getChildByName("profile"); - if (pl.hasValues()) { + List items = meta.children("profile"); + if (!items.isEmpty()) { XhtmlNode p = plateStyle(div.para()); - p.tx(Utilities.pluralize(context.formatPhrase(RenderingContext.GENERAL_PROF), pl.getValues().size())+": "); + p.tx(Utilities.pluralize(context.formatPhrase(RenderingContext.GENERAL_PROF), items.size())+": "); boolean first = true; - for (BaseWrapper bw : pl.getValues()) { + for (ResourceElement bw : items) { if (first) first = false; else p.tx(", "); - renderCanonical(r, p, bw.getBase().primitiveValue()); + renderCanonical(r, p, bw.primitiveValue()); } } - PropertyWrapper tl = meta.getChildByName("tag"); - if (tl.hasValues()) { + items = meta.children("tag"); + if (!items.isEmpty()) { XhtmlNode p = plateStyle(div.para()); - p.tx(Utilities.pluralize(context.formatPhrase(RenderingContext.RES_REND_TAG), tl.getValues().size())+": "); + p.tx(Utilities.pluralize(context.formatPhrase(RenderingContext.RES_REND_TAG), items.size())+": "); boolean first = true; - for (BaseWrapper bw : tl.getValues()) { + for (ResourceElement bw : items) { if (first) first = false; else p.tx(", "); - String system = getPrimitiveValue(bw, "system"); - String version = getPrimitiveValue(bw, "version"); - String code = getPrimitiveValue(bw, "system"); - String display = getPrimitiveValue(bw, "system"); - renderCoding(p, new Coding(system, version, code, display)); + renderCoding(status, p, bw); } } - PropertyWrapper sl = meta.getChildByName("security"); - if (sl.hasValues()) { + items = meta.children("security"); + if (!items.isEmpty()) { XhtmlNode p = plateStyle(div.para()); - p.tx(Utilities.pluralize(context.formatPhrase(RenderingContext.GENERAL_SECURITY_LABEL), tl.getValues().size())+": "); + p.tx(Utilities.pluralize(context.formatPhrase(RenderingContext.GENERAL_SECURITY_LABEL), items.size())+": "); boolean first = true; - for (BaseWrapper bw : sl.getValues()) { + for (ResourceElement bw : items) { if (first) first = false; else p.tx(", "); - String system = getPrimitiveValue(bw, "system"); - String version = getPrimitiveValue(bw, "version"); - String code = getPrimitiveValue(bw, "system"); - String display = getPrimitiveValue(bw, "system"); - renderCoding(p, new Coding(system, version, code, display)); + renderCoding(status, p, bw); } } } @@ -735,27 +751,27 @@ public abstract class ResourceRenderer extends DataRenderer { return r.has(name) && r.getChildByName(name).hasValues() ? r.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null; } - public void renderOrError(DomainResource dr) { - try { - render(dr); - } catch (Exception e) { - XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); - x.para().tx(context.formatPhrase(RenderingContext.RES_REND_ERROR, e.getMessage())+" "); - dr.setText(null); - inject(dr, x, NarrativeStatus.GENERATED); - } - - } +// public void renderOrError(DomainResource dr) { +// try { +// render(dr); +// } catch (Exception e) { +// XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); +// x.para().tx(context.formatPhrase(RenderingContext.RES_REND_ERROR, e.getMessage())+" "); +// dr.setText(null); +// inject(dr, x, NarrativeStatus.GENERATED); +// } +// +// } public RendererType getRendererType() { return RendererType.NATIVE; } public class TableRowData { - private Map> cols = new HashMap<>(); + private Map> cols = new HashMap<>(); private TableData data; - public void value(String name, DataType value) { + public void value(String name, ResourceElement value) { if (!cols.containsKey(name)) { cols.put(name, new ArrayList<>()); } @@ -769,7 +785,7 @@ public abstract class ResourceRenderer extends DataRenderer { return cols.containsKey(name); } - public List get(String name) { + public List get(String name) { return cols.get(name); } @@ -802,7 +818,7 @@ public abstract class ResourceRenderer extends DataRenderer { } - public void renderTable(TableData provider, XhtmlNode x) throws FHIRFormatError, DefinitionException, IOException { + public void renderTable(RenderingStatus status, TableData provider, XhtmlNode x) throws FHIRFormatError, DefinitionException, IOException { List columns = new ArrayList<>(); for (String name : provider.getColumns()) { boolean hasData = false; @@ -830,11 +846,11 @@ public abstract class ResourceRenderer extends DataRenderer { for (String col : columns) { XhtmlNode td = tr.td(); boolean first = true; - List list = row.get(col); + List list = row.get(col); if (list != null) { - for (DataType value : list) { + for (ResourceElement value : list) { if (first) first = false; else td.tx(", "); - render(td, value); + renderDataType(status, td, value); } } } @@ -842,13 +858,4 @@ public abstract class ResourceRenderer extends DataRenderer { } } - public void markLanguage(XhtmlNode x) { - x.setAttribute("lang", context.getLocale().toString()); - x.setAttribute("xml:lang", context.getLocale().toString()); - x.addTag(0, "hr"); - x.addTag(0, "p").b().tx(context.getLocale().getDisplayName()); - x.addTag(0, "hr"); - } - - } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java index 8624f069f..750421ae4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java @@ -34,7 +34,8 @@ import org.hl7.fhir.r5.model.CodeType; import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.DataType; -import org.hl7.fhir.r5.model.DecimalType; +import org.hl7.fhir.r5.model.DecimalType; +import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Element; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.AdditionalBindingPurposeVS; @@ -70,13 +71,15 @@ import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer.InternalMarkdownProcessor; import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer.RenderStyle; import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer.SourcedElementDefinition; -import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.ResourceElement; import org.hl7.fhir.r5.renderers.utils.RenderingContext.FixedValueFormat; import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.renderers.utils.RenderingContext.StructureDefinitionRendererMode; -import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; -import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; +import org.hl7.fhir.r5.renderers2.Renderer.RenderingStatus; +import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; +import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.PublicationHacker; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; @@ -101,6 +104,27 @@ import org.hl7.fhir.utilities.xhtml.XhtmlParser; public class StructureDefinitionRenderer extends ResourceRenderer { + public StructureDefinitionRenderer(RenderingContext context) { + super(context); + hostMd = new InternalMarkdownProcessor(); + corePath = context.getContext().getSpecUrl(); + } + + @Override + public void renderResource(RenderingStatus status, XhtmlNode x, ResourceElement r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { + throw new Error("StructureDefinitionRenderer only renders native resources directly"); + } + + @Override + public void renderResource(RenderingStatus status, XhtmlNode x, DomainResource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { + render(status, x, (StructureDefinition) r); + } + + @Override + public String displayResource(ResourceElement r) throws UnsupportedEncodingException, IOException { + return canonicalTitle(r); + } + public enum RenderStyle { } @@ -302,16 +326,6 @@ public class StructureDefinitionRenderer extends ResourceRenderer { private Map> sdMapCache = new HashMap<>(); private IMarkdownProcessor hostMd; - public StructureDefinitionRenderer(RenderingContext context) { - super(context); - hostMd = new InternalMarkdownProcessor(); - corePath = context.getContext().getSpecUrl(); - } - - public StructureDefinitionRenderer(RenderingContext context, ResourceContext rcontext) { - super(context, rcontext); - } - public Map> getSdMapCache() { return sdMapCache; @@ -329,18 +343,15 @@ public class StructureDefinitionRenderer extends ResourceRenderer { this.hostMd = hostMd; } - public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException { - return render(x, (StructureDefinition) dr); - } - public boolean render(XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException { + public void render(RenderingStatus status, XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException { if (context.getStructureMode() == StructureDefinitionRendererMode.DATA_DICT) { renderDict(sd, sd.getDifferential().getElement(), x.table("dict"), false, GEN_MODE_DIFF, ""); } else { x.getChildNodes().add(generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getLink(KnownLinkType.SPEC), "", sd.getKind() == StructureDefinitionKind.LOGICAL, false, null, false, context, "")); } - return true; + status.setExtensions(true); } public void describe(XhtmlNode x, StructureDefinition sd) { @@ -351,10 +362,6 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return sd.present(); } - @Override - public String display(Resource r) throws UnsupportedEncodingException, IOException { - return ((StructureDefinition) r).present(); - } public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException { if (r.has("title")) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java index a7cb6b4f9..1d16edf34 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java @@ -28,6 +28,7 @@ public class Resolver { String resolveUri(RenderingContext context, String uri); } + /* public static class ResourceContext { private ResourceContext container; @@ -183,7 +184,7 @@ public class Resolver { } } } - +*/ public enum ResourceReferenceKind { CONTAINED, BUNDLE, EXTERNAL, UNKNOWN @@ -194,9 +195,9 @@ public class Resolver { private ResourceReferenceKind kind; private String reference; - private ResourceWrapper resource; + private ResourceElement resource; - public ResourceWithReference(ResourceReferenceKind kind, String reference, ResourceWrapper resource) { + public ResourceWithReference(ResourceReferenceKind kind, String reference, ResourceElement resource) { super(); this.kind = kind; this.reference = reference; @@ -211,7 +212,7 @@ public class Resolver { return reference; } - public ResourceWrapper getResource() { + public ResourceElement getResource() { return resource; } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceElement.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceElement.java index 4eebe805e..8808961da 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceElement.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceElement.java @@ -6,16 +6,18 @@ import java.util.List; import java.util.Locale; import java.util.function.BooleanSupplier; +import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; +import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.SourcedChildDefinitions; import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.Narrative; import org.hl7.fhir.r5.model.Property; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.utils.client.network.FhirRequestBuilder; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; @@ -34,47 +36,78 @@ public class ResourceElement { BundleEntry, IndependentResource } + + public static class NamedResourceElementList { + private String name; + private List values = new ArrayList(); + + public NamedResourceElementList(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + public List getValues() { + return values; + } + + } - private ContextUtilities context; + private ContextUtilities contextUtils; + private ProfileUtilities profileUtils; private ResourceElement parent; private String name; // null at root private int index; // -1 if not repeating private ElementKind kind; + private StructureDefinition classDefinition; + private ElementDefinition propertyDefinition; private Base element; private Element model; private List children; - public ResourceElement(ContextUtilities context, Resource resource) { - this.context = context; + public ResourceElement(ContextUtilities contextUtils, ProfileUtilities profileUtils, Resource resource) { + this.contextUtils = contextUtils; + this.profileUtils = profileUtils; this.parent = null; this.name = null; this.index = -1; this.kind = ElementKind.IndependentResource; this.element = resource; + this.classDefinition = profileUtils.getContext().fetchTypeDefinition(resource.fhirType()); + this.propertyDefinition = this.classDefinition.getSnapshot().getElementFirstRep(); } - public ResourceElement(ContextUtilities context, DataType type) { - this.context = context; + public ResourceElement(ContextUtilities contextUtils, ProfileUtilities profileUtils, DataType type) { + this.contextUtils = contextUtils; + this.profileUtils = profileUtils; this.parent = null; this.name = null; this.index = -1; this.kind = null; this.element = type; + this.classDefinition = profileUtils.getContext().fetchTypeDefinition(type.fhirType()); + this.propertyDefinition = this.classDefinition.getSnapshot().getElementFirstRep(); } - public ResourceElement(ContextUtilities context, ResourceElement parent, String name, int index, ElementKind kind, Base element) { - this.context = context; + public ResourceElement(ContextUtilities contextUtils, ProfileUtilities profileUtils, ResourceElement parent, String name, int index, ElementKind kind, Base element, StructureDefinition classDefinition, ElementDefinition propertyDefinition) { + this.contextUtils = contextUtils; + this.profileUtils = profileUtils; this.parent = parent; this.name = name; this.index = index; this.kind = kind; this.element = element; + this.classDefinition = classDefinition; + this.propertyDefinition = propertyDefinition; } - public ResourceElement(ContextUtilities context, Element resource) { - this.context = context; + public ResourceElement(ContextUtilities contextUtils, ProfileUtilities profileUtils, Element resource) { + this.contextUtils = contextUtils; + this.profileUtils = profileUtils; this.parent = null; this.name = null; this.index = -1; @@ -82,13 +115,16 @@ public class ResourceElement { this.model = resource; } - public ResourceElement(ContextUtilities context, ResourceElement parent, String name, int index, ElementKind kind, Element em) { - this.context = context; + public ResourceElement(ContextUtilities contextUtils, ProfileUtilities profileUtils, ResourceElement parent, String name, int index, ElementKind kind, Element em) { + this.contextUtils = contextUtils; + this.profileUtils = profileUtils; this.parent = parent; this.name = name; this.index = index; this.kind = kind; this.model = em; + this.classDefinition = em.getProperty().getStructure(); + this.propertyDefinition = em.getProperty().getDefinition(); } public String fhirVersion() { @@ -201,7 +237,7 @@ public class ResourceElement { String name = child.getProperty().isChoice() ? child.getProperty().getName() : child.getName(); int index = child.isList() ? child.getIndex() : -1; ElementKind kind = determineModelKind(child); - children.add(new ResourceElement(context, this, name, index, kind, child)); + children.add(new ResourceElement(contextUtils, profileUtils, this, name, index, kind, child)); } } @@ -233,13 +269,29 @@ public class ResourceElement { } private void loadElementChildren() { + SourcedChildDefinitions childDefs = propertyDefinition == null ? null : profileUtils.getChildMap(classDefinition, propertyDefinition); for (Property p : element.children()) { String name = p.getName(); int i = 0; for (Base v : p.getValues()) { ElementKind kind = determineModelKind(p, v); int index = p.isList() ? i : -1; - children.add(new ResourceElement(context, this, name, index, kind, v)); + ElementDefinition ed = null; + if (childDefs != null) { + for (ElementDefinition t : childDefs.getList()) { + if (t.getName().equals(name)) { + ed = t; + break; + } + } + } + if (ed != null) { + children.add(new ResourceElement(contextUtils, profileUtils, this, name, index, kind, v, childDefs.getSource(), ed)); + } else { + StructureDefinition sd = profileUtils.getContext().fetchTypeDefinition(v.fhirType()); + ElementDefinition ted = sd.getSnapshot().getElementFirstRep(); + children.add(new ResourceElement(contextUtils, profileUtils, this, name, index, kind, v, sd, ted)); + } i++; } } @@ -248,7 +300,7 @@ public class ResourceElement { private ElementKind determineModelKind(Property p, Base v) { if (v.isPrimitive()) { return ElementKind.PrimitiveType; - } else if (context.isDatatype(v.fhirType())) { + } else if (contextUtils.isDatatype(v.fhirType())) { return ElementKind.DataType; } else if (!v.isResource()) { return ElementKind.BackboneElement; @@ -272,6 +324,25 @@ public class ResourceElement { return children; } + public List childrenInGroups() { + loadChildren(); + List list = new ArrayList(); + for (ResourceElement e : children) { + NamedResourceElementList nl = null; + for (NamedResourceElementList t : list) { + if (t.name.equals(e.name())) { + nl = t; + } + } + if (nl == null) { + nl = new NamedResourceElementList(e.name()); + list.add(nl); + } + nl.values.add(e); + } + return list; + } + public List children(String name) { loadChildren(); List list = new ArrayList(); @@ -454,7 +525,7 @@ public class ResourceElement { if (element != null) { return element instanceof DomainResource; } else { - return context.isDomainResource(fhirType()); + return contextUtils.isDomainResource(fhirType()); } } @@ -582,7 +653,7 @@ public class ResourceElement { } public ContextUtilities getContextUtilities() { - return context; + return contextUtils; } public boolean hasFormatComment() { @@ -601,5 +672,13 @@ public class ResourceElement { } } + public StructureDefinition getClassDefinition() { + return classDefinition; + } + + public ElementDefinition getPropertyDefinition() { + return propertyDefinition; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/rendering/ResourceElementTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/rendering/ResourceElementTests.java index 1444b1add..453901080 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/rendering/ResourceElementTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/rendering/ResourceElementTests.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.util.List; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Manager; @@ -26,7 +27,7 @@ public class ResourceElementTests { public void testDirect() throws FHIRFormatError, IOException { IWorkerContext worker = TestingUtilities.getSharedWorkerContext(); Resource res = new XmlParser().parse(TestingUtilities.loadTestResource("r5", "bundle-resource-element-test.xml")); - ResourceElement re = new ResourceElement(new ContextUtilities(worker), res); + ResourceElement re = new ResourceElement(new ContextUtilities(worker), new ProfileUtilities(worker, null, null), res); checkTree(re); } @@ -34,7 +35,7 @@ public class ResourceElementTests { public void testIndirect() throws FHIRFormatError, IOException { IWorkerContext worker = TestingUtilities.getSharedWorkerContext(); List res = Manager.parse(worker, TestingUtilities.loadTestResourceStream("r5", "bundle-resource-element-test.xml"), FhirFormat.XML); - ResourceElement re = new ResourceElement(new ContextUtilities(worker), res.get(0).getElement()); + ResourceElement re = new ResourceElement(new ContextUtilities(worker), new ProfileUtilities(worker, null, null), res.get(0).getElement()); checkTree(re); }