org.hl7.fhir.r5.renderers.BundleRenderer Maven / Gradle / Ivy
package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Provenance;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
@MarkedToMoveToAdjunctPackage
public class BundleRenderer extends ResourceRenderer {
public BundleRenderer(RenderingContext context) {
super(context);
}
@Override
public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
return context.formatPhrase(RenderingContext.BUNDLE_SUMMARY, getTranslatedCode(r.child("type")), r.children("entry").size());
}
public BundleRenderer setMultiLangMode(boolean multiLangMode) {
this.multiLangMode = multiLangMode;
return this;
}
@Override
public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper b) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
List entries = b.children("entry");
if ("collection".equals(b.primitiveValue("type")) && allEntriesAreHistoryProvenance(entries)) {
// nothing
} else {
int start = 0;
XhtmlNode root = x;
if ("document".equals(b.primitiveValue("type"))) {
if (entries.isEmpty() || (entries.get(0).has("resource") && !"Composition".equals(entries.get(0).child("resource").fhirType())))
throw new FHIRException(context.formatPhrase(RenderingContext.BUND_REND_INVALID_DOC, b.getId(), entries.get(0).child("resource").fhirType()+"')"));
renderDocument(status, root, b, entries);
if (!context.isTechnicalMode()) {
return;
}
start = 1;
root.hr();
root.h2().addText(formatPhrase(RenderingContext.BUNDLE_HEADER_DOCUMENT_CONTENTS));
} else {
renderResourceTechDetails(b, x);
root.para().addText(formatPhrase(RenderingContext.BUNDLE_HEADER_ROOT, b.getId(), b.primitiveValue("type")));
}
int i = 0;
for (ResourceWrapper be : entries) {
i++;
if (i >= start) {
String link = null;
if (be.has("fullUrl")) {
link = makeInternalBundleLink(b, be.primitiveValue("fullUrl"));
if (!context.hasAnchor(link)) {
context.addAnchor(link);
root.an(context.prefixAnchor(link));
}
}
ResourceWrapper res = be.child("resource");
if (be.has("resource")) {
String id = res.has("id") ? res.primitiveValue("id") : makeIdFromBundleEntry(be.primitiveValue("fullUrl"));
String anchor = res.fhirType() + "_" + id;
if (id != null && !context.hasAnchor(anchor)) {
context.addAnchor(anchor);
root.an(context.prefixAnchor(anchor));
}
anchor = "hc"+anchor;
if (id != null && !context.hasAnchor(anchor)) {
context.addAnchor(anchor);
root.an(context.prefixAnchor(anchor));
}
String ver = res.has("meta") ? res.child("meta").primitiveValue("version") : null;
if (ver != null) {
if (link != null) {
link = link + "/"+ver;
if (!context.hasAnchor(link)) {
context.addAnchor(link);
root.an(context.prefixAnchor(link));
}
}
if (id != null) {
anchor = anchor + "/"+ver;
if (!context.hasAnchor(anchor)) {
context.addAnchor(anchor);
root.an(context.prefixAnchor(anchor));
}
}
}
}
root.hr();
if (be.has("fullUrl")) {
root.para().addText(formatPhrase(RenderingContext.BUNDLE_HEADER_ENTRY_URL, Integer.toString(i), be.primitiveValue("fullUrl")));
} else {
root.para().addText(formatPhrase(RenderingContext.BUNDLE_HEADER_ENTRY, Integer.toString(i)));
}
if (be.has("search")) {
renderSearch(x, be.child("search"));
}
// if (be.hasRequest())
// renderRequest(root, be.getRequest());
// if (be.hasSearch())
// renderSearch(root, be.getSearch());
// if (be.hasResponse())
// renderResponse(root, be.getResponse());
if (be.has("resource")) {
ResourceWrapper r = res;
root.para().addText(formatPhrase(RenderingContext.BUNDLE_RESOURCE, r.fhirType()));
XhtmlNode xn = r.getNarrative();
if (xn == null || xn.isEmpty()) {
ResourceRenderer rr = RendererFactory.factory(r, context);
try {
xn = new XhtmlNode(NodeType.Element, "div");
rr.buildNarrative(new RenderingStatus(), xn, r);
} catch (Exception e) {
xn = new XhtmlNode();
xn.para().b().tx(context.formatPhrase(RenderingContext.BUNDLE_REV_EXCP, e.getMessage()) + " ");
}
} else {
xn.stripAnchorsByName(context.getAnchors());
}
root.blockquote().addChildren(xn);
}
if (be.has("request")) {
renderRequest(x, be.child("request"));
}
if (be.has("response")) {
renderResponse(x, be.child("response"));
}
}
}
}
}
private void renderDocument(RenderingStatus status, XhtmlNode x, ResourceWrapper b, List entries) throws UnsupportedEncodingException, FHIRException, IOException, EOperationOutcome {
// from the spec:
//
// When the document is presented for human consumption, applications SHOULD present the collated narrative portions in order:
// * The subject resource Narrative
// * The Composition resource Narrative
// * The section.text Narratives
ResourceWrapper comp = (ResourceWrapper) entries.get(0).child("resource");
XhtmlNode sum = renderResourceTechDetails(b, docSection(x, "Document Details"), comp.primitiveValueMN("title", "name"));
List subjectList = comp.children("subject");
if (sum != null) {
XhtmlNode p = sum.para();
p.startScript("doc");
renderDataType(status, p.param("status"), comp.child("status"));
renderDataType(status, p.param("date"), comp.child("date"));
renderDataTypes(status, p.param("author"), comp.children("author"));
renderDataTypes(status, p.param("subject"), subjectList);
if (comp.has("encounter")) {
renderDataType(status, p.param("encounter"), comp.child("encounter"));
p.paramValue("has-encounter", "true");
} else {
p.paramValue("has-encounter", "false");
}
p.execScript(context.formatMessage(RenderingContext.DOCUMENT_SUMMARY));
p.closeScript();
// status, type, category, subject, encounter, date, author,
x.hr();
}
List subjects = resolveReferences(entries, subjectList);
int i = 0;
for (ResourceWrapper subject : subjects) {
XhtmlNode sec = docSection(x, "Document Subject");
if (subject != null) {
if (subject.hasNarrative()) {
sec.addChildren(subject.getNarrative());
} else {
RendererFactory.factory(subject, context).buildNarrative(status, sec, subject);
}
} else {
sec.para().b().tx("Unable to resolve subject '"+displayReference(subjects.get(i))+"'");
}
i++;
}
x.hr();
XhtmlNode sec = docSection(x, "Document Content");
if (comp.hasNarrative()) {
sec.addChildren(comp.getNarrative());
sec.hr();
}
List sections = comp.children("section");
for (ResourceWrapper section : sections) {
addSection(status, sec, section, 2, false);
}
}
private void renderDataTypes(RenderingStatus status, XhtmlNode param, List children) throws FHIRFormatError, DefinitionException, IOException {
if (children != null && !children.isEmpty()) {
boolean first = true;
for (ResourceWrapper child : children) {
if (first) {first = false; } else {param.tx(", "); }
renderDataType(status, param, child);
}
}
}
private XhtmlNode docSection(XhtmlNode x, String name) {
XhtmlNode div = x.div();
div.style("border: 1px solid maroon; padding: 10px; background-color: #f2faf9; min-height: 160px;");
div.para().b().tx(name);
return div;
}
private void addSection(RenderingStatus status, XhtmlNode x, ResourceWrapper section, int level, boolean nested) throws UnsupportedEncodingException, FHIRException, IOException {
if (section.has("title") || section.has("code") || section.has("text") || section.has("section")) {
XhtmlNode div = x.div();
if (section.has("title")) {
div.h(level).tx(section.primitiveValue("title"));
} else if (section.has("code")) {
renderDataType(status, div.h(level), section.child("code"));
}
if (section.has("text")) {
ResourceWrapper narrative = section.child("text");
ResourceWrapper xh = narrative.child("div");
x.addChildren(xh.getXhtml());
}
if (section.has("section")) {
List sections = section.children("section");
for (ResourceWrapper child : sections) {
if (nested) {
addSection(status, x.blockquote().para(), child, level+1, true);
} else {
addSection(status, x, child, level+1, true);
}
}
}
}
// children
}
private List resolveReferences(List entries, List baselist) throws UnsupportedEncodingException, FHIRException, IOException {
List list = new ArrayList<>();
if (baselist != null) {
for (ResourceWrapper base : baselist) {
ResourceWrapper res = null;
ResourceWrapper prop = base.child("reference");
String ref = prop.primitiveValue();
if (prop != null && prop.hasPrimitiveValue()) {
for (ResourceWrapper entry : entries) {
if (entry.has("fullUrl")) {
String fu = entry.primitiveValue("fullUrl");
if (ref.equals(fu)) {
res = entry.child("resource");
}
}
if (entry.has("resource")) {
String type = entry.child("resource").fhirType();
String id = entry.child("resource").primitiveValue("id");
if (ref.equals(type+"/"+id)) {
res = entry.child("resource");
}
}
}
list.add(res);
}
}
}
return list;
}
private ResourceWrapper resolveReference(List entries, ResourceWrapper base) throws UnsupportedEncodingException, FHIRException, IOException {
if (base == null) {
return null;
}
ResourceWrapper prop = base.child("reference");
if (prop != null && prop.hasPrimitiveValue()) {
for (ResourceWrapper entry : entries) {
if (entry.has("fullUrl")) {
String fu = entry.primitiveValue("fullUrl");
if (prop.primitiveValue().equals(fu)) {
return entry.child("resource");
}
}
}
}
return null;
}
public static boolean allEntriesAreHistoryProvenance(List entries) throws UnsupportedEncodingException, FHIRException, IOException {
for (ResourceWrapper be : entries) {
if (!be.has("child") || !"Provenance".equals(be.child("resource").fhirType())) {
return false;
}
}
return !entries.isEmpty();
}
private boolean allEntresAreHistoryProvenance(Bundle b) {
for (BundleEntryComponent be : b.getEntry()) {
if (!(be.getResource() instanceof Provenance)) {
return false;
}
}
return !b.getEntry().isEmpty();
}
// private List checkInternalLinks(Bundle b, List childNodes) {
// scanNodesForInternalLinks(b, childNodes);
// return childNodes;
// }
//
// private void scanNodesForInternalLinks(Bundle b, List nodes) {
// for (XhtmlNode n : nodes) {
// if ("a".equals(n.getName()) && n.hasAttribute("href")) {
// scanInternalLink(b, n);
// }
// scanNodesForInternalLinks(b, n.getChildNodes());
// }
// }
//
// private void scanInternalLink(Bundle b, XhtmlNode n) {
// boolean fix = false;
// for (BundleEntryComponent be : b.getEntry()) {
// if (be.hasFullUrl() && be.getFullUrl().equals(n.getAttribute("href"))) {
// fix = true;
// }
// }
// if (fix) {
// n.setAttribute("href", "#"+makeInternalBundleLink(b, n.getAttribute("href")));
// }
// }
private void renderSearch(XhtmlNode root, ResourceWrapper search) {
StringBuilder b = new StringBuilder();
b.append(formatPhrase(RenderingContext.BUNDLE_SEARCH));
if (search.has("mode"))
b.append(formatPhrase(RenderingContext.BUNDLE_SEARCH_MODE, search.primitiveValue("mode")));
if (search.has("score")) {
if (search.has("mode")) {
b.append(",");
}
b.append(formatPhrase(RenderingContext.BUNDLE_SEARCH_SCORE, search.primitiveValue("score")));
}
root.para().addText(b.toString());
}
private void renderResponse(XhtmlNode root, ResourceWrapper response) {
root.para().addText(formatPhrase(RenderingContext.BUNDLE_RESPONSE));
StringBuilder b = new StringBuilder();
b.append(response.primitiveValue("status")+"\r\n");
if (response.has("location"))
b.append(formatPhrase(RenderingContext.BUNDLE_LOCATION, response.primitiveValue("location"))+"\r\n");
if (response.has("etag"))
b.append(formatPhrase(RenderingContext.BUNDLE_ETAG, response.primitiveValue("etag"))+"\r\n");
if (response.has("lastModified"))
b.append(formatPhrase(RenderingContext.BUNDLE_LAST_MOD, response.primitiveValue("lastModified"))+"\r\n");
root.pre().addText(b.toString());
}
private void renderRequest(XhtmlNode root, ResourceWrapper request) {
root.para().addText(formatPhrase(RenderingContext.BUNDLE_REQUEST));
StringBuilder b = new StringBuilder();
b.append(request.primitiveValue("method")+" "+request.primitiveValue("url")+"\r\n");
if (request.has("ifNoneMatch"))
b.append(formatPhrase(RenderingContext.BUNDLE_IF_NON_MATCH, request.primitiveValue("ifNoneMatch"))+"\r\n");
if (request.has("ifModifiedSince"))
b.append(formatPhrase(RenderingContext.BUNDLE_IF_MOD, request.primitiveValue("ifModifiedSince"))+"\r\n");
if (request.has("ifMatch"))
b.append(formatPhrase(RenderingContext.BUNDLE_IF_MATCH, request.primitiveValue("ifMatch"))+"\r\n");
if (request.has("ifNoneExist"))
b.append(formatPhrase(RenderingContext.BUNDLE_IF_NONE, request.primitiveValue("ifNoneExist"))+"\r\n");
root.pre().addText(b.toString());
}
public boolean canRender(Bundle b) {
for (BundleEntryComponent be : b.getEntry()) {
if (be.hasResource()) {
ResourceRenderer rr = RendererFactory.factory(be.getResource(), context);
if (!rr.canRender(be.getResource())) {
return false;
}
}
}
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy