Fix up rendering of Extensions
This commit is contained in:
parent
fd6fe88528
commit
a964d4fcd5
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -206,6 +206,11 @@ public class DOMWrappers {
|
|||
return getTypeCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementDefinition getElementDefinition() {
|
||||
return definition;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ResourceWrapperElement extends WrapperBaseImpl implements ResourceWrapper {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -741,6 +741,12 @@ public class XhtmlNode implements IBaseXhtml {
|
|||
}
|
||||
|
||||
|
||||
public XhtmlNode backgroundColor(String color) {
|
||||
style("background-color: "+color);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue