All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hl7.fhir.r5.renderers.LibraryRenderer Maven / Gradle / Ivy

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.CanonicalResource;
import org.hl7.fhir.r5.model.Resource;
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.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;

@MarkedToMoveToAdjunctPackage
public class LibraryRenderer extends ResourceRenderer {

  private static final int DATA_IMG_SIZE_CUTOFF = 4000; 
  
  public LibraryRenderer(RenderingContext context) { 
    super(context); 
  } 
 
  @Override
  public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
    return canonicalTitle(r);
  }

  @Override
  public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper lib) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
    renderResourceTechDetails(lib, x);
    genSummaryTable(status, x, (CanonicalResource) lib.getResourceNative());
    List authors = lib.children("author");
    List editors = lib.children("editor");
    List reviewers = lib.children("reviewer");
    List endorsers = lib.children("endorser");
    if (!authors.isEmpty() || !editors.isEmpty() || !reviewers.isEmpty() || !endorsers.isEmpty()) {
      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(context.formatPhrase(RenderingContext.LIB_REND_PAR));
      XhtmlNode t = x.table("grid", false);
      for (ResourceWrapper cd : authors) {
        participantRow(status, t, (context.formatPhrase(RenderingContext.LIB_REND_AUT)), cd, email, phone, url);
      }

      for (ResourceWrapper cd : editors) {
        participantRow(status, t, (context.formatPhrase(RenderingContext.LIB_REND_ED)), cd, email, phone, url);
      }
      for (ResourceWrapper cd : reviewers) {
        participantRow(status, t, (context.formatPhrase(RenderingContext.LIB_REND_REV)), cd, email, phone, url);
      }
      for (ResourceWrapper cd : endorsers) {
        participantRow(status, t, (context.formatPhrase(RenderingContext.LIB_REND_END)), cd, email, phone, url);
      }
    }
    List artifacts = lib.children("relatedArtifact");
    if (!artifacts.isEmpty()) {
      x.h2().tx(context.formatPhrase(RenderingContext.LIB_REND_ART));
      XhtmlNode t = x.table("grid", false);
      boolean label = false;
      boolean display = false;
      boolean citation = false;
      for (ResourceWrapper ra : artifacts) {
        label = label || ra.has("label");
        display = display || ra.has("display");
        citation = citation || ra.has("citation");
      }
      for (ResourceWrapper ra : artifacts) {
        renderArtifact(status, t, ra, lib, label, display, citation);
      }      
    }
    List parameters = lib.children("parameter");
    if (!parameters.isEmpty()) {
      x.h2().tx(context.formatPhrase(RenderingContext.GENERAL_PARS));
      XhtmlNode t = x.table("grid", false);
      boolean doco = false;
      for (ResourceWrapper p : parameters) {
        doco = doco || p.has("documentation");
      }
      for (ResourceWrapper p : parameters) {
        renderParameter(t, p, doco);
      }      
    }
    List dataRequirements = lib.children("dataRequirement");
    if (!dataRequirements.isEmpty()) {
      x.h2().tx(context.formatPhrase(RenderingContext.LIB_REND_REQ));
      for (ResourceWrapper p : dataRequirements) {
        renderDataRequirement(status, x, p);
      }      
    }
    List contents = lib.children("content");
    if (!contents.isEmpty()) {
      x.h2().tx(context.formatPhrase(RenderingContext.LIB_REND_CONT));          
      boolean isCql = false;
      int counter = 0;
      for (ResourceWrapper p : contents) {
        renderAttachment(x, p, isCql, counter, lib.getId());
        isCql = isCql || (p.has("contentType") && p.primitiveValue("contentType").startsWith("text/cql"));
        counter++;
      }
    }
  }
    
  private boolean hasCT(List list, String type) throws UnsupportedEncodingException, FHIRException, IOException {
    for (ResourceWrapper cd : list) {
      List telecoms = cd.children("telecom");
      if (hasContactPoint(telecoms, type)) {
        return true;
      }
    }
    return false;
  }

  private boolean hasContactPoint(List list, String type) {
    for (ResourceWrapper cd : list) {
      for (ResourceWrapper t : cd.children("telecom")) {
        if (type.equals(t.primitiveValue("system"))) {
          return true;
        }
      }
    }
    return false;
  }

  private ResourceWrapper getContactPoint(List list, String type) {
    for (ResourceWrapper cd : list) {
      for (ResourceWrapper t : cd.children("telecom")) {
        if (type.equals(t.primitiveValue("system"))) {
          return t;
        }
      }
    }
    return null;
  }

  private void renderParameter(XhtmlNode t, ResourceWrapper p, boolean doco) throws UnsupportedEncodingException, FHIRException, IOException {
    XhtmlNode tr = t.tr();
    tr.td().tx(p.has("name") ? p.primitiveValue("name") : null);
    tr.td().tx(p.has("use") ? p.primitiveValue("use") : null);
    tr.td().tx(p.has("min") ? p.primitiveValue("min") : null);
    tr.td().tx(p.has("max") ? p.primitiveValue("max") : null);
    tr.td().tx(p.has("type") ? p.primitiveValue("type") : null);
    if (doco) {
      tr.td().tx(p.has("documentation") ? p.primitiveValue("documentation") : null);
    }
  }


  private void renderArtifact(RenderingStatus status, XhtmlNode t, ResourceWrapper ra, ResourceWrapper lib, boolean label, boolean display, boolean citation) throws UnsupportedEncodingException, FHIRException, IOException {
    XhtmlNode tr = t.tr();
    tr.td().tx(ra.has("type") ? getTranslatedCode(ra.child("type")) : null);
    if (label) {
      tr.td().tx(ra.has("label") ? ra.primitiveValue("label") : null);
    }
    if (display) {
      tr.td().tx(ra.has("display") ? ra.primitiveValue("display") : null);
    }
    if (citation) {
      tr.td().markdown(ra.has("citation") ? ra.primitiveValue("citation") : null, "Citation");
    }
    if (ra.has("resource")) {
      renderCanonical(status, tr.td(), Resource.class, ra.child("resource"));
    } else {
      tr.td().tx(ra.has("url") ? ra.primitiveValue("url") : null);
    }
  }

  private void participantRow(RenderingStatus status, XhtmlNode t, String label, ResourceWrapper cd, boolean email, boolean phone, boolean url) throws UnsupportedEncodingException, FHIRException, IOException {
    XhtmlNode tr = t.tr();
    tr.td().tx(label);
    tr.td().tx(cd.has("name") ? cd.primitiveValue("name") : null);
    List telecoms = cd.children("telecom");
    if (email) {
      renderContactPoint(status, tr.td(), getContactPoint(telecoms, "email"));
    }
    if (phone) {
      renderContactPoint(status, tr.td(), getContactPoint(telecoms, "phone"));
    }
    if (url) {
      renderContactPoint(status, tr.td(), getContactPoint(telecoms, "url"));
    }
  }


  private void renderAttachment(XhtmlNode x, ResourceWrapper att, boolean noShowData, int counter, String baseId) {
    String url = att.primitiveValue("url");
    String title = att.primitiveValue("title");
    String ct =  att.primitiveValue("contentType");
    
    boolean ref = !att.has("data") && att.has("url");
    if (ref) {
      XhtmlNode p = x.para();
      if (att.has("title")) {
        p.tx(title);
        p.tx(": ");
      }
      Resource res = context.getContext().fetchResource(Resource.class, url);
      if (res == null || !res.hasWebPath()) {
        p.code().ah(context.prefixLocalHref(url)).tx(url);        
      } else if (res instanceof CanonicalResource) {
        p.code().ah(context.prefixLocalHref(res.getWebPath())).tx(((CanonicalResource) res).present());        
      } else {
        p.code().ah(context.prefixLocalHref(res.getWebPath())).tx(url);        
      }
      p.tx(" (");
      p.code().tx(ct);
      p.tx(lang(att));
      p.tx(")");
    } else if (!att.has("data")) {
      XhtmlNode p = x.para();
      if (att.has("title")) {
        p.tx(title);
        p.tx(": ");
      }
      p.code().tx(context.formatPhrase(RenderingContext.LIB_REND_NOCONT));
      p.tx(" (");
      p.code().tx(ct);
      p.tx(lang(att));
      p.tx(")");
    } else {
      byte[] cnt = Base64.decodeBase64(att.primitiveValue("data"));
      String txt = getText(cnt);
      if (isImage(ct)) {
        XhtmlNode p = x.para();
        if (att.has("title")) {
          p.tx(title);
          p.tx(": (");
          p.code().tx(ct);
          p.tx(lang(att));
          p.tx(")");
        }
        else {
          p.code().tx(ct+lang(att));
        }
        if (cnt.length < LibraryRenderer.DATA_IMG_SIZE_CUTOFF) {
          x.img("data: "+ct+">;base64,"+b64(cnt), "data");
        } else {
          String filename = "Library-"+baseId+(counter == 0 ? "" : "-"+Integer.toString(counter))+"."+imgExtension(ct); 
          x.img(filename, "data");
        }        
      } else if (txt != null && !noShowData) {
        XhtmlNode p = x.para();
        if (att.has("title")) {
          p.tx(title);
          p.tx(": (");
          p.code().tx(ct);
          p.tx(lang(att));
          p.tx(")");
        }
        else {
          p.code().tx(ct+lang(att));
        }
        String prismCode = determinePrismCode(ct);
        if (prismCode != null && !tooBig(txt)) {
          x.pre().code().setAttribute("class", "language-"+prismCode).tx(txt);
        } else {
          x.pre().code().tx(txt);
        }
      } else {
        XhtmlNode p = x.para();
        if (att.has("title")) {
          p.tx(title);
          p.tx(": ");
        }
        p.code().tx(context.formatPhrase(RenderingContext.LIB_REND_SHOW));
        p.code().tx(ct);
        p.tx(lang(att));
        p.tx((context.formatPhrase(RenderingContext.LIB_REND_SIZE, Utilities.describeSize(cnt.length))+" ")+")");
      }
    }    
  }

  private boolean tooBig(String txt) {
    return txt.length() > 16384;
  }

  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(ResourceWrapper att) {
    if (att.has("language")) {
      return ", language = "+describeLang(att.primitiveValue("language"));
    }
    return "";
  }

  private String getText( byte[] cnt) {
    try {
      try {
        String src = new String(cnt, "UTF-8");
        if (checkString(src)) {
          return src;
        }
      } catch (Exception e) {
        // ignore
      }
      try {
        String src = new String(cnt, "UTF-16");
        if (checkString(src)) {
          return src;
        }
      } catch (Exception e) {
        // ignore
      }
      try {
        String src = new String(cnt, "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(String ct) {
    if (!Utilities.noString(ct)) {
      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 (ct.contains("json+") || ct.contains("+json")) {
        return "json";
      }
      if (ct.contains("xml+") || ct.contains("+xml")) {
        return "xml";
      }
    }
    return null;
  }
  
  
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy