fix rendering of Documents
This commit is contained in:
parent
c7e8ffff09
commit
3fad39620b
|
@ -8,6 +8,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
import org.hl7.fhir.r5.elementmodel.Element;
|
import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
|
import org.hl7.fhir.r5.model.Base;
|
||||||
import org.hl7.fhir.r5.model.Bundle;
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
|
||||||
import org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent;
|
import org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent;
|
||||||
|
@ -15,8 +16,11 @@ import org.hl7.fhir.r5.model.Bundle.BundleEntryResponseComponent;
|
||||||
import org.hl7.fhir.r5.model.Bundle.BundleEntrySearchComponent;
|
import org.hl7.fhir.r5.model.Bundle.BundleEntrySearchComponent;
|
||||||
import org.hl7.fhir.r5.model.Bundle.BundleType;
|
import org.hl7.fhir.r5.model.Bundle.BundleType;
|
||||||
import org.hl7.fhir.r5.model.Composition;
|
import org.hl7.fhir.r5.model.Composition;
|
||||||
|
import org.hl7.fhir.r5.model.Composition.SectionComponent;
|
||||||
import org.hl7.fhir.r5.model.DomainResource;
|
import org.hl7.fhir.r5.model.DomainResource;
|
||||||
|
import org.hl7.fhir.r5.model.Property;
|
||||||
import org.hl7.fhir.r5.model.Provenance;
|
import org.hl7.fhir.r5.model.Provenance;
|
||||||
|
import org.hl7.fhir.r5.model.Reference;
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
|
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
|
||||||
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
|
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
|
||||||
|
@ -58,8 +62,7 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
if ("document".equals(b.get("type").primitiveValue())) {
|
if ("document".equals(b.get("type").primitiveValue())) {
|
||||||
if (entries.isEmpty() || (entries.get(0).has("resource") && "Composition".equals(entries.get(0).get("resource").fhirType())))
|
if (entries.isEmpty() || (entries.get(0).has("resource") && "Composition".equals(entries.get(0).get("resource").fhirType())))
|
||||||
throw new FHIRException("Invalid document - first entry is not a Composition");
|
throw new FHIRException("Invalid document - first entry is not a Composition");
|
||||||
ResourceWrapper r = (ResourceWrapper) entries.get(0).getChildByName("resource").getValues().get(0);
|
return renderDocument(x, b, entries);
|
||||||
x.addChildren(r.getNarrative());
|
|
||||||
} else if ("collection".equals(b.get("type").primitiveValue()) && allEntriesAreHistoryProvenance(entries)) {
|
} else if ("collection".equals(b.get("type").primitiveValue()) && allEntriesAreHistoryProvenance(entries)) {
|
||||||
// nothing
|
// nothing
|
||||||
} else {
|
} else {
|
||||||
|
@ -69,7 +72,7 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
for (BaseWrapper be : entries) {
|
for (BaseWrapper be : entries) {
|
||||||
i++;
|
i++;
|
||||||
if (be.has("fullUrl")) {
|
if (be.has("fullUrl")) {
|
||||||
root.an(makeInternalLink(be.get("fullUrl").primitiveValue()));
|
root.an(makeInternalBundleLink(be.get("fullUrl").primitiveValue()));
|
||||||
}
|
}
|
||||||
if (be.has("resource") && be.getChildByName("resource").getValues().get(0).has("id")) {
|
if (be.has("resource") && be.getChildByName("resource").getValues().get(0).has("id")) {
|
||||||
root.an(be.get("resource").fhirType() + "_" + be.getChildByName("resource").getValues().get(0).get("id").primitiveValue());
|
root.an(be.get("resource").fhirType() + "_" + be.getChildByName("resource").getValues().get(0).get("id").primitiveValue());
|
||||||
|
@ -89,7 +92,17 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
if (be.has("resource")) {
|
if (be.has("resource")) {
|
||||||
root.para().addText(formatMessage(RENDER_BUNDLE_RESOURCE, be.get("resource").fhirType()));
|
root.para().addText(formatMessage(RENDER_BUNDLE_RESOURCE, be.get("resource").fhirType()));
|
||||||
ResourceWrapper rw = be.getChildByName("resource").getAsResource();
|
ResourceWrapper rw = be.getChildByName("resource").getAsResource();
|
||||||
root.blockquote().addChildren(rw.getNarrative());
|
XhtmlNode xn = rw.getNarrative();
|
||||||
|
if (xn == null || xn.isEmpty()) {
|
||||||
|
ResourceRenderer rr = RendererFactory.factory(rw, context);
|
||||||
|
try {
|
||||||
|
xn = rr.render(rw);
|
||||||
|
} catch (Exception e) {
|
||||||
|
xn = new XhtmlNode();
|
||||||
|
xn.para().b().tx("Exception generating narrative: "+e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.blockquote().addChildren(xn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,13 +110,154 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean renderDocument(XhtmlNode x, ResourceWrapper b, List<BaseWrapper> 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).getChildByName("resource").getValues().get(0);
|
||||||
|
ResourceWrapper subject = resolveReference(entries, comp.get("subject"));
|
||||||
|
if (subject != null) {
|
||||||
|
if (subject.hasNarrative()) {
|
||||||
|
x.addChildren(subject.getNarrative());
|
||||||
|
} else {
|
||||||
|
RendererFactory.factory(subject, context).render(x, subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.hr();
|
||||||
|
if (comp.hasNarrative()) {
|
||||||
|
x.addChildren(comp.getNarrative());
|
||||||
|
x.hr();
|
||||||
|
}
|
||||||
|
List<BaseWrapper> sections = comp.children("section");
|
||||||
|
for (BaseWrapper section : sections) {
|
||||||
|
addSection(x, section, 2, false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSection(XhtmlNode x, BaseWrapper 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.get("title").primitiveValue());
|
||||||
|
} else if (section.has("code")) {
|
||||||
|
renderBase(div.h(level), section.get("code"));
|
||||||
|
}
|
||||||
|
if (section.has("text")) {
|
||||||
|
Base narrative = section.get("text");
|
||||||
|
x.addChildren(narrative.getXhtml());
|
||||||
|
}
|
||||||
|
if (section.has("section")) {
|
||||||
|
List<BaseWrapper> sections = section.children("section");
|
||||||
|
for (BaseWrapper child : sections) {
|
||||||
|
if (nested) {
|
||||||
|
addSection(x.blockquote(), child, level+1, true);
|
||||||
|
} else {
|
||||||
|
addSection(x, child, level+1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// children
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceWrapper resolveReference(List<BaseWrapper> entries, Base base) throws UnsupportedEncodingException, FHIRException, IOException {
|
||||||
|
Property prop = base.getChildByName("reference");
|
||||||
|
if (prop.hasValues()) {
|
||||||
|
String ref = prop.getValues().get(0).primitiveValue();
|
||||||
|
if (ref != null) {
|
||||||
|
for (BaseWrapper entry : entries) {
|
||||||
|
if (entry.has("fullUrl")) {
|
||||||
|
String fu = entry.get("fullUrl").primitiveValue();
|
||||||
|
if (ref.equals(fu)) {
|
||||||
|
return (ResourceWrapper) entry.getChildByName("resource").getValues().get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean renderDocument(XhtmlNode x, Bundle b) 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
|
||||||
|
Composition comp = (Composition) b.getEntry().get(0).getResource();
|
||||||
|
Resource subject = resolveReference(b, comp.getSubject());
|
||||||
|
if (subject != null) {
|
||||||
|
XhtmlNode nx = (subject instanceof DomainResource) ? ((DomainResource) subject).getText().getDiv() : null;
|
||||||
|
if (nx != null) {
|
||||||
|
x.addChildren(nx);
|
||||||
|
} else {
|
||||||
|
RendererFactory.factory(subject, context).render(x, subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.hr();
|
||||||
|
if (comp.getText().hasDiv()) {
|
||||||
|
x.addChildren(comp.getText().getDiv());
|
||||||
|
x.hr();
|
||||||
|
}
|
||||||
|
for (SectionComponent section : comp.getSection()) {
|
||||||
|
addSection(x, section, 2, false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Resource resolveReference(Bundle bnd, Reference reference) {
|
||||||
|
String ref = reference.getReference();
|
||||||
|
if (ref == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (BundleEntryComponent be : bnd.getEntry()) {
|
||||||
|
if (ref.equals(be.getFullUrl())) {
|
||||||
|
return be.getResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void addSection(XhtmlNode x, SectionComponent section, int level, boolean nested) throws UnsupportedEncodingException, FHIRException, IOException {
|
||||||
|
if (section.hasTitle() || section.hasCode() || section.hasText() || section.hasSection()) {
|
||||||
|
XhtmlNode div = x.div();
|
||||||
|
if (section.hasTitle()) {
|
||||||
|
div.h(level).tx(section.getTitle());
|
||||||
|
} else if (section.hasCode()) {
|
||||||
|
renderBase(div.h(level), section.getCode());
|
||||||
|
}
|
||||||
|
if (section.hasText()) {
|
||||||
|
x.addChildren(section.getText().getDiv());
|
||||||
|
}
|
||||||
|
if (section.hasSection()) {
|
||||||
|
List<SectionComponent> sections = section.getSection();
|
||||||
|
for (SectionComponent child : sections) {
|
||||||
|
if (nested) {
|
||||||
|
addSection(x.blockquote(), child, level+1, true);
|
||||||
|
} else {
|
||||||
|
addSection(x, child, level+1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// children
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public XhtmlNode render(Bundle b) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
|
public XhtmlNode render(Bundle b) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
|
||||||
if (b.getType() == BundleType.DOCUMENT) {
|
if (b.getType() == BundleType.DOCUMENT) {
|
||||||
if (!b.hasEntry() || !(b.getEntryFirstRep().hasResource() && b.getEntryFirstRep().getResource() instanceof Composition)) {
|
if (!b.hasEntry() || !(b.getEntryFirstRep().hasResource() && b.getEntryFirstRep().getResource() instanceof Composition)) {
|
||||||
throw new FHIRException("Invalid document - first entry is not a Composition");
|
throw new FHIRException("Invalid document - first entry is not a Composition");
|
||||||
}
|
}
|
||||||
Composition dr = (Composition) b.getEntryFirstRep().getResource();
|
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
|
||||||
return dr.getText().getDiv();
|
renderDocument(x, b);
|
||||||
|
return x;
|
||||||
} else if ((b.getType() == BundleType.COLLECTION && allEntresAreHistoryProvenance(b))) {
|
} else if ((b.getType() == BundleType.COLLECTION && allEntresAreHistoryProvenance(b))) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -113,7 +267,7 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
for (BundleEntryComponent be : b.getEntry()) {
|
for (BundleEntryComponent be : b.getEntry()) {
|
||||||
i++;
|
i++;
|
||||||
if (be.hasFullUrl())
|
if (be.hasFullUrl())
|
||||||
root.an(makeInternalLink(be.getFullUrl()));
|
root.an(makeInternalBundleLink(be.getFullUrl()));
|
||||||
if (be.hasResource() && be.getResource().hasId())
|
if (be.hasResource() && be.getResource().hasId())
|
||||||
root.an(be.getResource().getResourceType().name() + "_" + be.getResource().getId());
|
root.an(be.getResource().getResourceType().name() + "_" + be.getResource().getId());
|
||||||
root.hr();
|
root.hr();
|
||||||
|
@ -130,10 +284,22 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
renderResponse(root, be.getResponse());
|
renderResponse(root, be.getResponse());
|
||||||
if (be.hasResource()) {
|
if (be.hasResource()) {
|
||||||
root.para().addText(formatMessage(RENDER_BUNDLE_RESOURCE, be.getResource().fhirType()));
|
root.para().addText(formatMessage(RENDER_BUNDLE_RESOURCE, be.getResource().fhirType()));
|
||||||
if (be.hasResource() && be.getResource() instanceof DomainResource) {
|
if (be.hasResource()) {
|
||||||
|
XhtmlNode xn = null;
|
||||||
|
if (be.getResource() instanceof DomainResource) {
|
||||||
DomainResource dr = (DomainResource) be.getResource();
|
DomainResource dr = (DomainResource) be.getResource();
|
||||||
if ( dr.getText().hasDiv())
|
xn = dr.getText().getDiv();
|
||||||
root.blockquote().getChildNodes().addAll(checkInternalLinks(b, dr.getText().getDiv().getChildNodes()));
|
}
|
||||||
|
if (xn == null || xn.isEmpty()) {
|
||||||
|
ResourceRenderer rr = RendererFactory.factory(be.getResource(), context);
|
||||||
|
try {
|
||||||
|
xn = rr.build(be.getResource());
|
||||||
|
} catch (Exception e) {
|
||||||
|
xn = new XhtmlNode();
|
||||||
|
xn.para().b().tx("Exception generating narrative: "+e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.blockquote().getChildNodes().addAll(checkInternalLinks(b, xn.getChildNodes()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +348,7 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fix) {
|
if (fix) {
|
||||||
n.setAttribute("href", "#"+makeInternalLink(n.getAttribute("href")));
|
n.setAttribute("href", "#"+makeInternalBundleLink(n.getAttribute("href")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,11 +394,6 @@ public class BundleRenderer extends ResourceRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private String makeInternalLink(String fullUrl) {
|
|
||||||
return fullUrl.replace(":", "-");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String display(Bundle bundle) throws UnsupportedEncodingException, IOException {
|
public String display(Bundle bundle) throws UnsupportedEncodingException, IOException {
|
||||||
return "??";
|
return "??";
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,5 +324,8 @@ public abstract class ResourceRenderer extends DataRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String makeInternalBundleLink(String fullUrl) {
|
||||||
|
return fullUrl.replace(":", "-");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -38,6 +38,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseXhtml;
|
import org.hl7.fhir.instance.model.api.IBaseXhtml;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
|
||||||
|
@ -476,6 +477,13 @@ public class XhtmlNode implements IBaseXhtml {
|
||||||
return addTag("h2");
|
return addTag("h2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XhtmlNode h(int level) {
|
||||||
|
if (level < 1 || level > 6) {
|
||||||
|
throw new FHIRException("Illegal Header level "+level);
|
||||||
|
}
|
||||||
|
return addTag("h"+Integer.toString(level));
|
||||||
|
}
|
||||||
|
|
||||||
public XhtmlNode h3() {
|
public XhtmlNode h3() {
|
||||||
return addTag("h3");
|
return addTag("h3");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue