Fix up rendering of Extensions

This commit is contained in:
Grahame Grieve 2020-09-04 16:22:09 +10:00
parent fd6fe88528
commit a964d4fcd5
8 changed files with 238 additions and 56 deletions

View File

@ -171,8 +171,9 @@ private Map<String, Object> userData;
List<Property> children = new ArrayList<Property>();
listChildren(children);
for (Property c : children)
if (c.getName().equals(name))
if (c.getName().equals(name) || c.getName().equals(name+"[x]")) {
return c;
}
return null;
}

View File

@ -14,10 +14,16 @@ import org.hl7.fhir.r5.model.Annotation;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.BaseDateTimeType;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ContactPoint;
import org.hl7.fhir.r5.model.DataRequirement;
import org.hl7.fhir.r5.model.DataRequirement.DataRequirementCodeFilterComponent;
import org.hl7.fhir.r5.model.DataRequirement.DataRequirementDateFilterComponent;
import org.hl7.fhir.r5.model.DataRequirement.DataRequirementSortComponent;
import org.hl7.fhir.r5.model.DataRequirement.SortDirection;
import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DateTimeType;
@ -778,6 +784,102 @@ public class DataRenderer extends Renderer {
x.addText(!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay());
}
public void renderDataRequirement(XhtmlNode x, DataRequirement dr) {
XhtmlNode tbl = x.table("grid");
XhtmlNode tr = tbl.tr();
XhtmlNode td = tr.td().colspan("2");
td.b().tx("Type");
td.tx(": ");
StructureDefinition sd = context.getWorker().fetchTypeDefinition(dr.getType().toCode());
if (sd != null && sd.hasUserData("path")) {
td.ah(sd.getUserString("path")).tx(dr.getType().toCode());
} else {
td.tx(dr.getType().toCode());
}
if (dr.hasProfile()) {
td.tx(" (");
boolean first = true;
for (CanonicalType p : dr.getProfile()) {
if (first) first = false; else td.tx(" | ");
sd = context.getWorker().fetchResource(StructureDefinition.class, p.getValue());
if (sd != null && sd.hasUserData("path")) {
td.ah(sd.getUserString("path")).tx(sd.present());
} else {
td.tx(p.asStringValue());
}
}
td.tx(")");
}
if (dr.hasSubject()) {
tr = tbl.tr();
td = tr.td().colspan("2");
td.b().tx("Subject");
if (dr.hasSubjectReference()) {
renderReference(td, dr.getSubjectReference());
} else {
renderCodeableConcept(td, dr.getSubjectCodeableConcept());
}
}
if (dr.hasCodeFilter() || dr.hasDateFilter()) {
tr = tbl.tr().backgroundColor("#efefef");
tr.td().tx("Filter");
tr.td().tx("Value");
}
for (DataRequirementCodeFilterComponent cf : dr.getCodeFilter()) {
tr = tbl.tr();
if (cf.hasPath()) {
tr.td().tx(cf.getPath());
} else {
tr.td().tx("Search on " +cf.getSearchParam());
}
if (cf.hasValueSet()) {
td = tr.td();
td.tx("In ValueSet ");
render(td, cf.getValueSetElement());
} else {
boolean first = true;
td = tr.td();
td.tx("One of these codes: ");
for (Coding c : cf.getCode()) {
if (first) first = false; else td.tx(", ");
render(td, c);
}
}
}
for (DataRequirementDateFilterComponent cf : dr.getDateFilter()) {
tr = tbl.tr();
if (cf.hasPath()) {
tr.td().tx(cf.getPath());
} else {
tr.td().tx("Search on " +cf.getSearchParam());
}
render(tr.td(), cf.getValue());
}
if (dr.hasSort() || dr.hasLimit()) {
tr = tbl.tr();
td = tr.td().colspan("2");
if (dr.hasLimit()) {
td.b().tx("Limit");
td.tx(": ");
td.tx(dr.getLimit());
if (dr.hasSort()) {
td.tx(", ");
}
}
if (dr.hasSort()) {
td.b().tx("Sort");
td.tx(": ");
boolean first = true;
for (DataRequirementSortComponent p : dr.getSort()) {
if (first) first = false; else td.tx(" | ");
td.tx(p.getDirection() == SortDirection.ASCENDING ? "+" : "-");
td.tx(p.getPath());
}
}
}
}
private String displayTiming(Timing s) throws FHIRException {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
if (s.hasCode())

View File

@ -27,6 +27,7 @@ import org.hl7.fhir.r5.model.CodeableReference;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ContactDetail;
import org.hl7.fhir.r5.model.ContactPoint;
import org.hl7.fhir.r5.model.DataRequirement;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Dosage;
@ -82,6 +83,7 @@ import org.w3c.dom.Element;
public class ProfileDrivenRenderer extends ResourceRenderer {
private Set<String> containedIds = new HashSet<>();
private boolean hasExtensions;
public ProfileDrivenRenderer(RenderingContext context, ResourceContext rcontext) {
super(context, rcontext);
@ -108,12 +110,13 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
System.out.println("hah!");
}
containedIds.clear();
hasExtensions = false;
generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0);
} catch (Exception e) {
e.printStackTrace();
x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
}
return false;
return hasExtensions;
}
@Override
@ -378,6 +381,9 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
} else if (e instanceof Resource) {
return;
} else if (e instanceof DataRequirement) {
DataRequirement p = (DataRequirement) e;
renderDataRequirement(x, p);
} else if (e instanceof ElementDefinition) {
x.tx("todo-bundle");
} else if (e != null && !(e instanceof Attachment) && !(e instanceof Narrative) && !(e instanceof Meta)) {
@ -386,7 +392,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
throw new NotImplementedException("type "+e.getClass().getName()+" not handled yet, and no structure found");
else
generateByProfile(res, sd, ew, sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep(),
getChildrenForPath(sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep().getPath()), x, path, showCodeDetails, indent + 1);
getChildrenForPath(sd.getSnapshot().getElement(), sd.getSnapshot().getElementFirstRep().getPath()), x, e.fhirType(), showCodeDetails, indent + 1);
}
}
@ -612,65 +618,98 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
for (PropertyWrapper p : splitExtensions(profile, e.children())) {
if (p.hasValues()) {
ElementDefinition child = getElementDefinition(children, path+"."+p.getName(), p);
if (child == null) {
child = p.getElementDefinition();
}
if (child != null) {
Map<String, String> displayHints = readDisplayHints(child);
if ("DomainResource.contained".equals(child.getBase().getPath())) {
generateElementByProfile(res, profile, allElements, x, path, showCodeDetails, indent, p, child);
}
}
}
}
}
public void generateElementByProfile(ResourceWrapper res, StructureDefinition profile, List<ElementDefinition> allElements, XhtmlNode x, String path,
boolean showCodeDetails, int indent, PropertyWrapper p, ElementDefinition child) throws UnsupportedEncodingException, IOException, EOperationOutcome {
Map<String, String> displayHints = readDisplayHints(child);
if ("DomainResource.contained".equals(child.getBase().getPath())) {
// if (p.getValues().size() > 0 && child != null) {
// for (BaseWrapper v : p.getValues()) {
// x.an(v.get("id").primitiveValue());
// }
// }
} else if (!exemptFromRendering(child)) {
List<ElementDefinition> grandChildren = getChildrenForPath(allElements, path+"."+p.getName());
filterGrandChildren(grandChildren, path+"."+p.getName(), p);
if (p.getValues().size() > 0) {
if (isPrimitive(child)) {
XhtmlNode para = x.para();
String name = p.getName();
if (name.endsWith("[x]"))
name = name.substring(0, name.length() - 3);
if (showCodeDetails || !isDefaultValue(displayHints, p.getValues())) {
para.b().addText(name);
para.tx(": ");
if (renderAsList(child) && p.getValues().size() > 1) {
XhtmlNode list = x.ul();
for (BaseWrapper v : p.getValues())
renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent);
} else {
boolean first = true;
for (BaseWrapper v : p.getValues()) {
if (first)
first = false;
else
para.tx(", ");
renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent);
}
}
}
} else if (canDoTable(path, p, grandChildren)) {
x.addTag(getHeader()).addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
XhtmlNode tbl = x.table( "grid");
XhtmlNode tr = tbl.tr();
tr.td().tx("-"); // work around problem with empty table rows
addColumnHeadings(tr, grandChildren);
for (BaseWrapper v : p.getValues()) {
if (v != null) {
tr = tbl.tr();
tr.td().tx("*"); // work around problem with empty table rows
addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent);
}
}
} else {
for (BaseWrapper v : p.getValues()) {
if (v != null) {
XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(p.getName());
generateByProfile(res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1);
}
}
} else if (!exemptFromRendering(child)) {
if (isExtension(p)) {
hasExtensions = true;
}
List<ElementDefinition> grandChildren = getChildrenForPath(allElements, path+"."+p.getName());
filterGrandChildren(grandChildren, path+"."+p.getName(), p);
if (p.getValues().size() > 0) {
if (isPrimitive(child)) {
XhtmlNode para = x.para();
String name = p.getName();
if (name.endsWith("[x]"))
name = name.substring(0, name.length() - 3);
if (showCodeDetails || !isDefaultValue(displayHints, p.getValues())) {
para.b().addText(name);
para.tx(": ");
if (renderAsList(child) && p.getValues().size() > 1) {
XhtmlNode list = x.ul();
for (BaseWrapper v : p.getValues())
renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent);
} else {
boolean first = true;
for (BaseWrapper v : p.getValues()) {
if (first)
first = false;
else
para.tx(", ");
renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent);
}
}
}
} else if (canDoTable(path, p, grandChildren)) {
x.addTag(getHeader()).addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName()))));
XhtmlNode tbl = x.table( "grid");
XhtmlNode tr = tbl.tr();
tr.td().tx("-"); // work around problem with empty table rows
addColumnHeadings(tr, grandChildren);
for (BaseWrapper v : p.getValues()) {
if (v != null) {
tr = tbl.tr();
tr.td().tx("*"); // work around problem with empty table rows
addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent);
}
}
} else if (isExtension(p)) {
for (BaseWrapper v : p.getValues()) {
if (v != null) {
PropertyWrapper vp = v.getChildByName("value");
PropertyWrapper ev = v.getChildByName("extension");
if (vp.hasValues()) {
BaseWrapper vv = vp.value();
XhtmlNode para = x.para();
para.b().addText(p.getStructure().present());
para.tx(": ");
renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent);
} else if (ev.hasValues()) {
XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName());
for (BaseWrapper vv : ev.getValues()) {
StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension");
List<ElementDefinition> children = getChildrenForPath(ex.getSnapshot().getElement(), "Extension");
generateByProfile(res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1);
}
}
}
}
} else {
for (BaseWrapper v : p.getValues()) {
if (v != null) {
XhtmlNode bq = x.addTag("blockquote");
bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName());
generateByProfile(res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1);
}
}
}
}
@ -699,6 +738,9 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
}
private boolean canDoTable(String path, PropertyWrapper p, List<ElementDefinition> grandChildren) {
if (isExtension(p)) {
return false;
}
for (ElementDefinition e : grandChildren) {
List<PropertyWrapper> values = getValues(path, p, e);
if (values.size() > 1 || !isPrimitive(e) || !canCollapse(e))
@ -707,6 +749,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
return true;
}
public boolean isExtension(PropertyWrapper p) {
return p.getName().contains("extension[");
}
private boolean canCollapse(ElementDefinition e) {
// we can collapse any data type
@ -792,10 +838,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
if (url.startsWith("http://hl7.org/fhir") && !url.startsWith("http://hl7.org/fhir/us"))
throw new DefinitionException("unknown extension "+url);
// System.out.println("unknown extension "+url);
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", p.getTypeCode(), p.getDefinition(), p.getMinCardinality(), p.getMaxCardinality(), ex));
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", p.getTypeCode(), p.getDefinition(), p.getMinCardinality(), p.getMaxCardinality(), ex), ed.getSnapshot().getElementFirstRep());
} else {
ElementDefinition def = ed.getSnapshot().getElement().get(0);
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", "Extension", def.getDefinition(), def.getMin(), def.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(def.getMax()), ex));
pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", "Extension", def.getDefinition(), def.getMin(), def.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(def.getMax()), ex), ed.getSnapshot().getElementFirstRep());
((PropertyWrapperDirect) pe).getWrapped().setStructure(ed);
}
results.add(pe);

View File

@ -8,6 +8,7 @@ import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
import org.hl7.fhir.r5.renderers.ResourceRenderer;
@ -28,6 +29,7 @@ public class BaseWrappers {
public int getMinCardinality();
public int getMaxCardinality();
public StructureDefinition getStructure();
public ElementDefinition getElementDefinition();
public BaseWrapper value();
public ResourceWrapper getAsResource();
public String fhirType();

View File

@ -206,6 +206,11 @@ public class DOMWrappers {
return getTypeCode();
}
@Override
public ElementDefinition getElementDefinition() {
return definition;
}
}
public static class ResourceWrapperElement extends WrapperBaseImpl implements ResourceWrapper {

View File

@ -8,6 +8,7 @@ import java.util.List;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Encounter;
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
import org.hl7.fhir.r5.model.Patient;
@ -29,6 +30,7 @@ public class DirectWrappers {
public static class PropertyWrapperDirect extends RendererWrapperImpl implements PropertyWrapper {
private Property wrapped;
private List<BaseWrapper> list;
private ElementDefinition ed;
public PropertyWrapperDirect(RenderingContext context, Property wrapped) {
super(context);
@ -37,6 +39,14 @@ public class DirectWrappers {
this.wrapped = wrapped;
}
public PropertyWrapperDirect(RenderingContext context, Property wrapped, ElementDefinition ed) {
super(context);
if (wrapped == null)
throw new Error("wrapped == null");
this.wrapped = wrapped;
this.ed = ed;
}
@Override
public String getName() {
return wrapped.getName();
@ -106,6 +116,11 @@ public class DirectWrappers {
public String fhirType() {
return wrapped.getTypeCode();
}
@Override
public ElementDefinition getElementDefinition() {
return ed;
}
}
public static class BaseWrapperDirect extends WrapperBaseImpl implements BaseWrapper {

View File

@ -58,7 +58,7 @@ public class ElementWrappers {
throw new FHIRException(e.getMessage(), e);
}
if (context.getParser() == null) {
System.out.println("Noe version specific parser provided");
System.out.println("No version specific parser provided");
}
if (context.getParser() == null) {
throw new Error("No type parser provided to renderer context");
@ -324,6 +324,11 @@ public class ElementWrappers {
return getTypeCode();
}
@Override
public ElementDefinition getElementDefinition() {
return definition;
}
}
}

View File

@ -741,6 +741,12 @@ public class XhtmlNode implements IBaseXhtml {
}
public XhtmlNode backgroundColor(String color) {
style("background-color: "+color);
return this;
}