Merge pull request #351 from hapifhir/gg-library-rendering

GG library rendering
This commit is contained in:
Grahame Grieve 2020-09-22 03:20:13 +10:00 committed by GitHub
commit 233cf71630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 858 additions and 20 deletions

View File

@ -0,0 +1,11 @@
Validator:
* Fix handling resources in bundles when type is profiled
* Prevent NPE resolving resource in batch
* fix value set validation for primitive types when an expansion is provided, and the code system is not known
Other Changes:
* Package Subsystem - Support wildcars for patch version
* Renderer: Don't make a column for definitions in a code system if there are none
* Renderer: special case support for fr-CA language
* Renderer: Prevent NPE when auto-generating narrative and an illegal resource type is encountered
* FHIRPath Engine: correction for allowing boolean conversion of primitive types

View File

@ -0,0 +1,26 @@
public ContactPoint getEmail() {
for (ContactPoint cp : getTelecom()) {
if (cp.getSystem() == ContactPointSystem.EMAIL) {
return cp;
}
}
return null;
}
public ContactPoint getPhone() {
for (ContactPoint cp : getTelecom()) {
if (cp.getSystem() == ContactPointSystem.PHONE) {
return cp;
}
}
return null;
}
public ContactPoint getUrl() {
for (ContactPoint cp : getTelecom()) {
if (cp.getSystem() == ContactPointSystem.URL) {
return cp;
}
}
return null;
}

View File

@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.r5.model.Enumerations.*;
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
import org.hl7.fhir.exceptions.FHIRException;
@ -308,6 +309,32 @@ public class ContactDetail extends DataType implements ICompositeType {
return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(name, telecom);
}
public ContactPoint getEmail() {
for (ContactPoint cp : getTelecom()) {
if (cp.getSystem() == ContactPointSystem.EMAIL) {
return cp;
}
}
return null;
}
public ContactPoint getPhone() {
for (ContactPoint cp : getTelecom()) {
if (cp.getSystem() == ContactPointSystem.PHONE) {
return cp;
}
}
return null;
}
public ContactPoint getUrl() {
for (ContactPoint cp : getTelecom()) {
if (cp.getSystem() == ContactPointSystem.URL) {
return cp;
}
}
return null;
}
}

View File

@ -184,8 +184,18 @@ public class DataRenderer extends Renderer {
lang = lang.substring(0, lang.indexOf("-"));
}
for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) {
if (cc.getCode().equals(lang) || cc.getCode().startsWith(lang+"-"))
if (cc.getCode().equals(lang)) {
l = cc;
break;
}
}
if (l == null) {
for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) {
if (cc.getCode().startsWith(lang+"-")) {
l = cc;
break;
}
}
}
}
if (l != null) {
@ -689,9 +699,63 @@ public class DataRenderer extends Renderer {
}
protected void renderContactPoint(XhtmlNode x, ContactPoint contact) {
if (contact != null) {
if (!contact.hasSystem()) {
x.addText(displayContactPoint(contact));
} else {
switch (contact.getSystem()) {
case EMAIL:
x.ah("mailto:"+contact.getValue()).tx(contact.getValue());
break;
case FAX:
x.addText(displayContactPoint(contact));
break;
case NULL:
x.addText(displayContactPoint(contact));
break;
case OTHER:
x.addText(displayContactPoint(contact));
break;
case PAGER:
x.addText(displayContactPoint(contact));
break;
case PHONE:
if (contact.hasValue() && contact.getValue().startsWith("+")) {
x.ah("tel:"+contact.getValue().replace(" ", "")).tx(contact.getValue());
} else {
x.addText(displayContactPoint(contact));
}
break;
case SMS:
x.addText(displayContactPoint(contact));
break;
case URL:
x.ah(contact.getValue()).tx(contact.getValue());
break;
default:
break;
}
}
}
}
protected void displayContactPoint(XhtmlNode p, ContactPoint c) {
if (c != null) {
if (c.getSystem() == ContactPointSystem.PHONE) {
p.tx("Phone: "+c.getValue());
} else if (c.getSystem() == ContactPointSystem.FAX) {
p.tx("Fax: "+c.getValue());
} else if (c.getSystem() == ContactPointSystem.EMAIL) {
p.tx(c.getValue());
} else if (c.getSystem() == ContactPointSystem.URL) {
if (c.getValue().length() > 30) {
p.addText(c.getValue().substring(0, 30)+"...");
} else {
p.addText(c.getValue());
}
}
}
}
protected void addTelecom(XhtmlNode p, ContactPoint c) {
if (c.getSystem() == ContactPointSystem.PHONE) {

View File

@ -0,0 +1,533 @@
package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.commons.codec.binary.Base64;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.Annotation;
import org.hl7.fhir.r5.model.Attachment;
import org.hl7.fhir.r5.model.Base;
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.DomainResource;
import org.hl7.fhir.r5.model.Library;
import org.hl7.fhir.r5.model.ListResource;
import org.hl7.fhir.r5.model.ListResource.ListResourceEntryComponent;
import org.hl7.fhir.r5.model.ParameterDefinition;
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.renderers.utils.BaseWrappers.BaseWrapper;
import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper;
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.Resolver.ResourceWithReference;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class LibraryRenderer extends ResourceRenderer {
private static final int DATA_IMG_SIZE_CUTOFF = 4000;
public LibraryRenderer(RenderingContext context) {
super(context);
}
public LibraryRenderer(RenderingContext context, ResourceContext rcontext) {
super(context, rcontext);
}
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (Library) dr);
}
public boolean render(XhtmlNode x, ResourceWrapper lib) throws FHIRFormatError, DefinitionException, IOException {
PropertyWrapper authors = lib.getChildByName("author");
PropertyWrapper editors = lib.getChildByName("editor");
PropertyWrapper reviewers = lib.getChildByName("reviewer");
PropertyWrapper endorsers = lib.getChildByName("endorser");
if ((authors != null && authors.hasValues()) || (editors != null && editors.hasValues()) || (reviewers != null && reviewers.hasValues()) || (endorsers != null && endorsers.hasValues())) {
boolean email = hasCT(authors, "email") || hasCT(editors, "email") || hasCT(reviewers, "email") || hasCT(endorsers, "email");
boolean phone = hasCT(authors, "phone") || hasCT(editors, "phone") || hasCT(reviewers, "phone") || hasCT(endorsers, "phone");
boolean url = hasCT(authors, "url") || hasCT(editors, "url") || hasCT(reviewers, "url") || hasCT(endorsers, "url");
x.h2().tx("Participants");
XhtmlNode t = x.table("grid");
if (authors != null) {
for (BaseWrapper cd : authors.getValues()) {
participantRow(t, "Author", cd, email, phone, url);
}
}
if (authors != null) {
for (BaseWrapper cd : editors.getValues()) {
participantRow(t, "Editor", cd, email, phone, url);
}
}
if (authors != null) {
for (BaseWrapper cd : reviewers.getValues()) {
participantRow(t, "Reviewer", cd, email, phone, url);
}
}
if (authors != null) {
for (BaseWrapper cd : endorsers.getValues()) {
participantRow(t, "Endorser", cd, email, phone, url);
}
}
}
PropertyWrapper artifacts = lib.getChildByName("relatedArtifact");
if (artifacts != null && artifacts.hasValues()) {
x.h2().tx("Related Artifacts");
XhtmlNode t = x.table("grid");
boolean label = false;
boolean display = false;
boolean citation = false;
for (BaseWrapper ra : artifacts.getValues()) {
label = label || ra.has("label");
display = display || ra.has("display");
citation = citation || ra.has("citation");
}
for (BaseWrapper ra : artifacts.getValues()) {
renderArtifact(t, ra, lib, label, display, citation);
}
}
PropertyWrapper parameters = lib.getChildByName("parameter");
if (parameters != null && parameters.hasValues()) {
x.h2().tx("Parameters");
XhtmlNode t = x.table("grid");
boolean doco = false;
for (BaseWrapper p : parameters.getValues()) {
doco = doco || p.has("documentation");
}
for (BaseWrapper p : parameters.getValues()) {
renderParameter(t, p, doco);
}
}
PropertyWrapper dataRequirements = lib.getChildByName("dataRequirement");
if (dataRequirements != null && dataRequirements.hasValues()) {
x.h2().tx("Data Requirements");
for (BaseWrapper p : dataRequirements.getValues()) {
renderDataRequirement(x, (DataRequirement) p.getBase());
}
}
PropertyWrapper contents = lib.getChildByName("content");
if (contents != null) {
x.h2().tx("Contents");
boolean isCql = false;
int counter = 0;
for (BaseWrapper p : contents.getValues()) {
Attachment att = (Attachment) p.getBase();
renderAttachment(x, att, isCql, counter, lib.getId());
isCql = isCql || (att.hasContentType() && att.getContentType().startsWith("text/cql"));
counter++;
}
}
return false;
}
private boolean hasCT(PropertyWrapper prop, String type) throws UnsupportedEncodingException, FHIRException, IOException {
if (prop != null) {
for (BaseWrapper cd : prop.getValues()) {
PropertyWrapper telecoms = cd.getChildByName("telecom");
if (getContactPoint(telecoms, type) != null) {
return true;
}
}
}
return false;
}
private boolean hasCT(List<ContactDetail> list, String type) {
for (ContactDetail cd : list) {
for (ContactPoint t : cd.getTelecom()) {
if (type.equals(t.getSystem().toCode())) {
return true;
}
}
}
return false;
}
public boolean render(XhtmlNode x, Library lib) throws FHIRFormatError, DefinitionException, IOException {
if (lib.hasAuthor() || lib.hasEditor() || lib.hasReviewer() || lib.hasEndorser()) {
boolean email = hasCT(lib.getAuthor(), "email") || hasCT(lib.getEditor(), "email") || hasCT(lib.getReviewer(), "email") || hasCT(lib.getEndorser(), "email");
boolean phone = hasCT(lib.getAuthor(), "phone") || hasCT(lib.getEditor(), "phone") || hasCT(lib.getReviewer(), "phone") || hasCT(lib.getEndorser(), "phone");
boolean url = hasCT(lib.getAuthor(), "url") || hasCT(lib.getEditor(), "url") || hasCT(lib.getReviewer(), "url") || hasCT(lib.getEndorser(), "url");
x.h2().tx("Participants");
XhtmlNode t = x.table("grid");
for (ContactDetail cd : lib.getAuthor()) {
participantRow(t, "Author", cd, email, phone, url);
}
for (ContactDetail cd : lib.getEditor()) {
participantRow(t, "Editor", cd, email, phone, url);
}
for (ContactDetail cd : lib.getReviewer()) {
participantRow(t, "Reviewer", cd, email, phone, url);
}
for (ContactDetail cd : lib.getEndorser()) {
participantRow(t, "Endorser", cd, email, phone, url);
}
}
if (lib.hasRelatedArtifact()) {
x.h2().tx("Related Artifacts");
XhtmlNode t = x.table("grid");
boolean label = false;
boolean display = false;
boolean citation = false;
for (RelatedArtifact ra : lib.getRelatedArtifact()) {
label = label || ra.hasLabel();
display = display || ra.hasDisplay();
citation = citation || ra.hasCitation();
}
for (RelatedArtifact ra : lib.getRelatedArtifact()) {
renderArtifact(t, ra, lib, label, display, citation);
}
}
if (lib.hasParameter()) {
x.h2().tx("Parameters");
XhtmlNode t = x.table("grid");
boolean doco = false;
for (ParameterDefinition p : lib.getParameter()) {
doco = doco || p.hasDocumentation();
}
for (ParameterDefinition p : lib.getParameter()) {
renderParameter(t, p, doco);
}
}
if (lib.hasDataRequirement()) {
x.h2().tx("Data Requirements");
for (DataRequirement p : lib.getDataRequirement()) {
renderDataRequirement(x, p);
}
}
if (lib.hasContent()) {
x.h2().tx("Contents");
boolean isCql = false;
int counter = 0;
for (Attachment att : lib.getContent()) {
renderAttachment(x, att, isCql, counter, lib.getId());
isCql = isCql || (att.hasContentType() && att.getContentType().startsWith("text/cql"));
counter++;
}
}
return false;
}
private void renderParameter(XhtmlNode t, BaseWrapper p, boolean doco) throws UnsupportedEncodingException, FHIRException, IOException {
XhtmlNode tr = t.tr();
tr.td().tx(p.has("name") ? p.get("name").primitiveValue() : null);
tr.td().tx(p.has("use") ? p.get("use").primitiveValue() : null);
tr.td().tx(p.has("min") ? p.get("min").primitiveValue() : null);
tr.td().tx(p.has("max") ? p.get("max").primitiveValue() : null);
tr.td().tx(p.has("type") ? p.get("type").primitiveValue() : null);
if (doco) {
tr.td().tx(p.has("documentation") ? p.get("documentation").primitiveValue() : null);
}
}
private void renderParameter(XhtmlNode t, ParameterDefinition p, boolean doco) {
XhtmlNode tr = t.tr();
tr.td().tx(p.getName());
tr.td().tx(p.getUse().getDisplay());
tr.td().tx(p.getMin());
tr.td().tx(p.getMax());
tr.td().tx(p.getType().getDisplay());
if (doco) {
tr.td().tx(p.getDocumentation());
}
}
private void renderArtifact(XhtmlNode t, BaseWrapper ra, ResourceWrapper lib, boolean label, boolean display, boolean citation) throws UnsupportedEncodingException, FHIRException, IOException {
XhtmlNode tr = t.tr();
tr.td().tx(ra.has("type") ? ra.get("type").primitiveValue() : null);
if (label) {
tr.td().tx(ra.has("label") ? ra.get("label").primitiveValue() : null);
}
if (display) {
tr.td().tx(ra.has("display") ? ra.get("display").primitiveValue() : null);
}
if (citation) {
tr.td().markdown(ra.has("citation") ? ra.get("citation").primitiveValue() : null, "Citation");
}
if (ra.has("resource")) {
renderCanonical(lib, tr.td(), ra.get("resource").primitiveValue());
} else {
tr.td().tx(ra.has("url") ? ra.get("url").primitiveValue() : null);
}
}
private void renderArtifact(XhtmlNode t, RelatedArtifact ra, Resource lib, boolean label, boolean display, boolean citation) throws IOException {
XhtmlNode tr = t.tr();
tr.td().tx(ra.getType().getDisplay());
if (label) {
tr.td().tx(ra.getLabel());
}
if (display) {
tr.td().tx(ra.getDisplay());
}
if (citation) {
tr.td().markdown(ra.getCitation(), "Citation");
}
if (ra.hasResource()) {
renderCanonical(lib, tr.td(), ra.getResource());
} else {
tr.td().tx(ra.getUrl());
}
}
private void participantRow(XhtmlNode t, String label, BaseWrapper cd, boolean email, boolean phone, boolean url) throws UnsupportedEncodingException, FHIRException, IOException {
XhtmlNode tr = t.tr();
tr.td().tx(label);
tr.td().tx(cd.get("name") != null ? cd.get("name").primitiveValue() : null);
PropertyWrapper telecoms = cd.getChildByName("telecom");
if (email) {
renderContactPoint(tr.td(), getContactPoint(telecoms, "email"));
}
if (phone) {
renderContactPoint(tr.td(), getContactPoint(telecoms, "phone"));
}
if (url) {
renderContactPoint(tr.td(), getContactPoint(telecoms, "url"));
}
}
private ContactPoint getContactPoint(PropertyWrapper telecoms, String value) throws UnsupportedEncodingException, FHIRException, IOException {
for (BaseWrapper t : telecoms.getValues()) {
if (t.has("system")) {
String system = t.get("system").primitiveValue();
if (value.equals(system)) {
return (ContactPoint) t.getBase();
}
}
}
return null;
}
private void participantRow(XhtmlNode t, String label, ContactDetail cd, boolean email, boolean phone, boolean url) {
XhtmlNode tr = t.tr();
tr.td().tx(label);
tr.td().tx(cd.getName());
if (email) {
renderContactPoint(tr.td(), cd.getEmail());
}
if (phone) {
renderContactPoint(tr.td(), cd.getPhone());
}
if (url) {
renderContactPoint(tr.td(), cd.getUrl());
}
}
public void describe(XhtmlNode x, Library lib) {
x.tx(display(lib));
}
public String display(Library lib) {
return lib.present();
}
@Override
public String display(Resource r) throws UnsupportedEncodingException, IOException {
return ((Library) r).present();
}
@Override
public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
if (r.has("title")) {
return r.children("title").get(0).getBase().primitiveValue();
}
return "??";
}
private void renderAttachment(XhtmlNode x, Attachment att, boolean noShowData, int counter, String baseId) {
boolean ref = !att.hasData() && att.hasUrl();
if (ref) {
XhtmlNode p = x.para();
if (att.hasTitle()) {
p.tx(att.getTitle());
p.tx(": ");
}
p.code().ah(att.getUrl()).tx(att.getUrl());
p.tx(" (");
p.code().tx(att.getContentType());
p.tx(lang(att));
p.tx(")");
} else if (!att.hasData()) {
XhtmlNode p = x.para();
if (att.hasTitle()) {
p.tx(att.getTitle());
p.tx(": ");
}
p.code().tx("No Content");
p.tx(" (");
p.code().tx(att.getContentType());
p.tx(lang(att));
p.tx(")");
} else {
String txt = getText(att);
if (isImage(att.getContentType())) {
XhtmlNode p = x.para();
if (att.hasTitle()) {
p.tx(att.getTitle());
p.tx(": (");
p.code().tx(att.getContentType());
p.tx(lang(att));
p.tx(")");
}
else {
p.code().tx(att.getContentType()+lang(att));
}
if (att.getData().length < LibraryRenderer.DATA_IMG_SIZE_CUTOFF) {
x.img("data: "+att.getContentType()+">;base64,"+b64(att.getData()));
} else {
String filename = "Library-"+baseId+(counter == 0 ? "" : "-"+Integer.toString(counter))+"."+imgExtension(att.getContentType());
x.img(filename);
}
} else if (txt != null && !noShowData) {
XhtmlNode p = x.para();
if (att.hasTitle()) {
p.tx(att.getTitle());
p.tx(": (");
p.code().tx(att.getContentType());
p.tx(lang(att));
p.tx(")");
}
else {
p.code().tx(att.getContentType()+lang(att));
}
String prismCode = determinePrismCode(att);
if (prismCode != null) {
x.pre().code().setAttribute("class", "language-"+prismCode).tx(txt);
} else {
x.pre().code().tx(txt);
}
} else {
XhtmlNode p = x.para();
if (att.hasTitle()) {
p.tx(att.getTitle());
p.tx(": ");
}
p.code().tx("Content not shown - (");
p.code().tx(att.getContentType());
p.tx(lang(att));
p.tx(", size = "+Utilities.describeSize(att.getData().length)+")");
}
}
}
private String imgExtension(String contentType) {
if (contentType != null && contentType.startsWith("image/")) {
if (contentType.startsWith("image/png")) {
return "png";
}
if (contentType.startsWith("image/jpeg")) {
return "jpg";
}
}
return null;
}
private String b64(byte[] data) {
byte[] encodeBase64 = Base64.encodeBase64(data);
return new String(encodeBase64);
}
private boolean isImage(String contentType) {
return imgExtension(contentType) != null;
}
private String lang(Attachment att) {
if (att.hasLanguage()) {
return ", language = "+describeLang(att.getLanguage());
}
return "";
}
private String getText(Attachment att) {
try {
try {
String src = new String(att.getData(), "UTF-8");
if (checkString(src)) {
return src;
}
} catch (Exception e) {
// ignore
}
try {
String src = new String(att.getData(), "UTF-16");
if (checkString(src)) {
return src;
}
} catch (Exception e) {
// ignore
}
try {
String src = new String(att.getData(), "ASCII");
if (checkString(src)) {
return src;
}
} catch (Exception e) {
// ignore
}
return null;
} catch (Exception e) {
return null;
}
}
public boolean checkString(String src) {
for (char ch : src.toCharArray()) {
if (ch < ' ' && ch != '\r' && ch != '\n' && ch != '\t') {
return false;
}
}
return true;
}
private String determinePrismCode(Attachment att) {
if (att.hasContentType()) {
String ct = att.getContentType();
if (ct.contains(";")) {
ct = ct.substring(0, ct.indexOf(";"));
}
switch (ct) {
case "text/html" : return "html";
case "text/xml" : return "xml";
case "application/xml" : return "xml";
case "text/markdown" : return "markdown";
case "application/js" : return "JavaScript";
case "application/css" : return "css";
case "text/x-csrc" : return "c";
case "text/x-csharp" : return "csharp";
case "text/x-c++src" : return "cpp";
case "application/graphql" : return "graphql";
case "application/x-java" : return "java";
case "application/json" : return "json";
case "text/json" : return "json";
case "application/liquid" : return "liquid";
case "text/x-pascal" : return "pascal";
case "text/x-python" : return "python";
case "text/x-rsrc" : return "r";
case "text/x-ruby" : return "ruby";
case "text/x-sas" : return "sas";
case "text/x-sql" : return "sql";
case "application/typescript" : return "typescript";
case "text/cql" : return "sql"; // not that bad...
}
if (att.getContentType().contains("json+") || att.getContentType().contains("+json")) {
return "json";
}
if (att.getContentType().contains("xml+") || att.getContentType().contains("+xml")) {
return "xml";
}
}
return null;
}
}

View File

@ -392,6 +392,8 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else if (e instanceof DataRequirement) {
DataRequirement p = (DataRequirement) e;
renderDataRequirement(x, p);
} else if (e instanceof PrimitiveType) {
x.tx(((PrimitiveType) e).primitiveValue());
} else if (e instanceof ElementDefinition) {
x.tx("todo-bundle");
} else if (e != null && !(e instanceof Attachment) && !(e instanceof Narrative) && !(e instanceof Meta)) {
@ -486,7 +488,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
renderAddress(x, (Address) e);
return true;
} else if (e instanceof ContactPoint) {
if (allowLinks) {
renderContactPoint(x, (ContactPoint) e);
} else {
displayContactPoint(x, (ContactPoint) e);
}
return true;
} else if (e instanceof Timing) {
renderTiming(x, (Timing) e);
@ -528,7 +534,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
boolean first = true;
for (ContactPoint c : cd.getTelecom()) {
if (first) first = false; else x.tx(",");
if (allowLinks) {
renderContactPoint(x, c);
} else {
displayContactPoint(x, c);
}
}
return true;
} else if (e instanceof Range) {

View File

@ -59,6 +59,9 @@ public class RendererFactory {
if ("Encounter".equals(resourceName)) {
return new EncounterRenderer(context);
}
if ("Library".equals(resourceName)) {
return new LibraryRenderer(context);
}
if ("List".equals(resourceName)) {
return new ListRenderer(context);
}
@ -102,6 +105,9 @@ public class RendererFactory {
if ("List".equals(resource.getName())) {
return new ListRenderer(context);
}
if ("Library".equals(resource.getName())) {
return new LibraryRenderer(context);
}
if ("DiagnosticReport".equals(resource.getName())) {
return new DiagnosticReportRenderer(context);
}

View File

@ -125,6 +125,44 @@ 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);
}
public void renderCanonical(ResourceWrapper rw, XhtmlNode x, String url, boolean allowLinks) throws UnsupportedEncodingException, IOException {
if (url == null) {
return;
}
Resource target = context.getWorker().fetchResource(Resource.class, url);
if (target == null || !(target instanceof CanonicalResource)) {
x.code().tx(url);
} else {
CanonicalResource cr = (CanonicalResource) target;
if (url.contains("|")) {
if (target.hasUserData("path")) {
x.ah(target.getUserString("path")).tx(cr.present()+" (version "+cr.getVersion()+")");
} else {
url = url.substring(0, url.indexOf("|"));
x.code().tx(url);
x.tx(": "+cr.present()+" (version "+cr.getVersion()+")");
}
} else {
if (target.hasUserData("path")) {
x.ah(target.getUserString("path")).tx(cr.present());
} else {
url = url.substring(0, url.indexOf("|"));
x.code().tx(url);
x.tx(": "+cr.present());
}
}
}
}
public void renderReference(Resource res, XhtmlNode x, Reference r) throws UnsupportedEncodingException, IOException {
ResourceWrapper rw = new ResourceWrapperDirect(this.context, res);
renderReference(rw, x, r);

View File

@ -52,6 +52,7 @@ public class BaseWrappers {
public void describe(XhtmlNode x) throws UnsupportedEncodingException, IOException;
public void injectNarrative(XhtmlNode x, NarrativeStatus status) throws IOException;
public BaseWrapper root();
public PropertyWrapper getChildByName(String tail);
public StructureDefinition getDefinition();
public boolean hasNarrative();
}

View File

@ -362,6 +362,16 @@ public class DOMWrappers {
public String fhirType() {
return wrapped.getNodeName();
}
@Override
public PropertyWrapper getChildByName(String name) {
for (PropertyWrapper p : children())
if (p.getName().equals(name))
return p;
return null;
}
}
}

View File

@ -267,6 +267,16 @@ public class DirectWrappers {
public String fhirType() {
return wrapped.fhirType();
}
@Override
public PropertyWrapper getChildByName(String name) {
Property p = wrapped.getChildByName(name);
if (p == null)
return null;
else
return new PropertyWrapperDirect(context, p);
}
}
}

View File

@ -246,8 +246,17 @@ public class ElementWrappers {
public String fhirType() {
return wrapped.fhirType();
}
@Override
public PropertyWrapper getChildByName(String name) {
for (PropertyWrapper p : children())
if (p.getName().equals(name))
return p;
return null;
}
}
public static class PropertyWrapperMetaElement extends RendererWrapperImpl implements PropertyWrapper {
private StructureDefinition structure;

View File

@ -90,4 +90,49 @@ public class MimeType {
return source;
}
public static String getExtension(String mimeType) {
MimeType mt = new MimeType(mimeType);
return mt.getExtension();
}
public String getExtension() {
switch (base) {
case "text/html" : return "html";
case "text/xml" : return "xml";
case "application/xml" : return "xml";
case "text/markdown" : return "md";
case "application/js" : return "js";
case "application/css" : return "css";
case "text/x-csrc" : return "c";
case "text/x-csharp" : return "cs";
case "text/x-c++src" : return "c";
case "application/graphql" : return "graphql";
case "application/x-java" : return "java";
case "application/json" : return "json";
case "text/json" : return "json";
case "application/liquid" : return "liquid";
case "text/x-pascal" : return "pas";
case "text/x-python" : return "py";
case "text/x-rsrc" : return "r";
case "text/x-ruby" : return "ruby";
case "text/x-sas" : return "sas";
case "text/x-sql" : return "sql";
case "application/typescript" : return "ts";
case "text/cql": return "cql";
case "image/png": return "png";
case "image/gif": return "gif";
case "image/jpeg": return "jpg";
}
if (base.contains("xml+") || base.contains("+xml")) {
return "xml";
}
if (base.contains("json+") || base.contains("+json")) {
return "json";
}
if (base.contains("turtle+") || base.contains("+turtle")) {
return "ttl";
}
return null;
}
}

View File

@ -47,6 +47,8 @@ import java.math.RoundingMode;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
@ -320,13 +322,13 @@ public class Utilities {
destFile.createNewFile();
}
FileChannel source = null;
FileChannel destination = null;
FileInputStream source = null;
FileOutputStream destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
destination.transferFrom(source, 0, source.size());
source = new FileInputStream(sourceFile);
destination = new FileOutputStream(destFile);
destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size());
} finally {
if (source != null) {
source.close();

View File

@ -25,7 +25,7 @@ import com.google.gson.JsonObject;
public class PackageHacker {
public static void main(String[] args) throws FileNotFoundException, IOException {
new PackageHacker().edit("M:\\web\\hl7.org\\fhir\\uv\\cdisc-lab\\package.tgz");
new PackageHacker().edit("M:\\web\\hl7.org\\fhir\\us\\carin-rtpbc\\package.tgz");
}
private void edit(String name) throws FileNotFoundException, IOException {
@ -58,9 +58,9 @@ public class PackageHacker {
private void change(JsonObject npm, Map<String, byte[]> content) throws FileNotFoundException, IOException {
fixVersions(npm);
npm.remove("url");
npm.addProperty("url", "http://hl7.org/fhir/uv/cdisc-lab/STU1");
npm.remove("name");
npm.addProperty("name", "hl7.fhir.uv.cdisc-lab");
npm.addProperty("url", "http://hl7.org/fhir/us/carin-rtpbc/STU1");
// npm.remove("name");
// npm.addProperty("name", "hl7.fhir.uv.smart-app-launch");
// npm.remove("canonical");
// npm.addProperty("canonical", "http://hl7.org/fhir/us/davinci-drug-formulary");
//// npm.remove("description");
@ -70,7 +70,7 @@ public class PackageHacker {
// npm.remove("dependencies");
// JsonObject dep = new JsonObject();
// npm.add("dependencies", dep);
// dep.addProperty("hl7.fhir.r4.core", "4.0.1");
// dep.addProperty("hl7.fhir.r3.core", "3.0.1");
// dep.addProperty("hl7.fhir.r4.examples", "4.0.1");
// dep.addProperty("hl7.fhir.r4.expansions", "4.0.1");
// dep.addProperty("hl7.fhir.r4.elements", "4.0.1");
@ -80,7 +80,7 @@ public class PackageHacker {
npm.remove("fhirVersions");
JsonArray a = new JsonArray();
npm.add("fhirVersions", a);
a.add("4.5.0");
a.add("3.0.1");
}
private void setProperty(JsonObject npm, String name, String value) {

View File

@ -38,9 +38,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.instance.model.api.IBaseXhtml;
import org.hl7.fhir.utilities.MarkDownProcessor;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.MarkDownProcessor.Dialect;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
@ -86,6 +90,9 @@ public class XhtmlNode implements IBaseXhtml {
private List<XhtmlNode> childNodes = new ArrayList<XhtmlNode>();
private String content;
private boolean notPretty;
private boolean inPara;
private boolean inLink;
public XhtmlNode() {
super();
@ -140,7 +147,7 @@ public class XhtmlNode implements IBaseXhtml {
return this;
}
public void validate(List<String> errors, String path, boolean inResource, boolean inPara) {
public void validate(List<String> errors, String path, boolean inResource, boolean inPara, boolean inLink) {
if (nodeType == NodeType.Element || nodeType == NodeType.Document) {
path = Utilities.noString(path) ? name : path+"/"+name;
if (inResource) {
@ -168,13 +175,19 @@ public class XhtmlNode implements IBaseXhtml {
if (inPara && Utilities.existsInList(name, "div", "blockquote", "table", "ol", "ul", "p")) {
errors.add("Error at "+path+": Found "+name+" inside an html paragraph");
}
if (inLink && Utilities.existsInList(name, "a")) {
errors.add("Error at "+path+": Found an <a> inside an <a> paragraph");
}
if (childNodes != null) {
if ("p".equals(name)) {
inPara = true;
}
if ("a".equals(name)) {
inLink = true;
}
for (XhtmlNode child : childNodes) {
child.validate(errors, path, inResource, inPara);
child.validate(errors, path, inResource, inPara, inLink);
}
}
}
@ -187,8 +200,20 @@ public class XhtmlNode implements IBaseXhtml {
throw new Error("Wrong node type - node is "+nodeType.toString()+" ('"+getName()+"/"+getContent()+"')");
}
// if (inPara && name.equals("p")) {
// throw new FHIRException("nested Para");
// }
// if (inLink && name.equals("a")) {
// throw new FHIRException("Nested Link");
// }
XhtmlNode node = new XhtmlNode(NodeType.Element);
node.setName(name);
if (inPara || name.equals("p")) {
node.inPara = true;
}
if (inLink || name.equals("a")) {
node.inLink = true;
}
childNodes.add(node);
return node;
}
@ -199,6 +224,12 @@ public class XhtmlNode implements IBaseXhtml {
if (!(nodeType == NodeType.Element || nodeType == NodeType.Document))
throw new Error("Wrong node type. is "+nodeType.toString());
XhtmlNode node = new XhtmlNode(NodeType.Element);
if (inPara || name.equals("p")) {
node.inPara = true;
}
if (inLink || name.equals("a")) {
node.inLink = true;
}
node.setName(name);
childNodes.add(index, node);
return node;
@ -796,6 +827,21 @@ public class XhtmlNode implements IBaseXhtml {
}
public void markdown(String md, String source) throws IOException {
if (md != null) {
String s = new MarkDownProcessor(Dialect.COMMON_MARK).process(md, source);
XhtmlParser p = new XhtmlParser();
XhtmlNode m;
try {
m = p.parse("<div>"+s+"</div>", "div");
} catch (org.hl7.fhir.exceptions.FHIRFormatError e) {
throw new FHIRFormatError(e.getMessage(), e);
}
getChildNodes().addAll(m.getChildNodes());
}
}

View File

@ -17,7 +17,7 @@
<properties>
<hapi_fhir_version>5.1.0</hapi_fhir_version>
<validator_test_case_version>1.1.42</validator_test_case_version>
<validator_test_case_version>1.1.43-SNAPSHOT</validator_test_case_version>
<junit_jupiter_version>5.6.2</junit_jupiter_version>
<maven_surefire_version>3.0.0-M4</maven_surefire_version>
<jacoco_version>0.8.5</jacoco_version>