001package org.hl7.fhir.r5.renderers;
002
003import java.io.IOException;
004import java.nio.charset.StandardCharsets;
005import java.util.ArrayList;
006import java.util.Base64;
007import java.util.List;
008import java.util.Map;
009
010import org.hl7.fhir.r5.elementmodel.Element;
011import org.hl7.fhir.r5.model.Binary;
012import org.hl7.fhir.utilities.FileUtilities;
013import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
014import org.hl7.fhir.utilities.Utilities;
015import org.hl7.fhir.utilities.xhtml.NodeType;
016import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
017import org.hl7.fhir.utilities.xhtml.XhtmlNode;
018
019@MarkedToMoveToAdjunctPackage
020public class BinaryRenderer {
021
022  private String folder;
023  private List<String> filenames = new ArrayList<String>();
024  private Map<String, String> scriptMappings;
025
026  public BinaryRenderer(String folder, Map<String, String> scriptMappings) {
027    super();
028    this.folder = folder;
029    this.scriptMappings = scriptMappings;
030  }
031
032  public String getFolder() {
033    return folder;
034  }
035
036  public List<String> getFilenames() {
037    return filenames;
038  }
039
040  public static String getBinContentAsString(byte[] bin) {
041    // for now, assume UTF8. To do: extract character encoding from mime type if possible (charset)
042    return new String(bin, StandardCharsets.UTF_8);
043  }
044
045  public String display(Binary bin) throws IOException {
046    XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
047    render(div, bin);
048    return new XhtmlComposer(false, true).compose(div);
049  }
050
051  public String display(Element bin) throws IOException {
052    XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
053    render(div, bin);
054    return new XhtmlComposer(false, true).compose(div);
055  }
056
057  public String display(String id, String ct, byte[] cnt) throws IOException {
058    XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
059    render(div, id, ct, cnt);
060    return new XhtmlComposer(false, true).compose(div);
061  }
062
063  public void render(XhtmlNode x, Binary bin) throws IOException {
064    String ct = bin.getContentType();
065    byte[] cnt = bin.getContent();
066    render(x, bin.getId(), ct, cnt);
067  }
068
069  public void render(XhtmlNode x, Element bin) throws IOException {
070    String ct = bin.getNamedChildValue("contentType");
071    String id = bin.getNamedChildValue("id");
072    byte[] cnt = Base64.getMimeDecoder().decode(bin.getNamedChildValue("data"));
073    render(x, id, ct, cnt);
074  }
075  
076  public void render(XhtmlNode x, String id, String ct, byte[] cnt) throws IOException {
077    filenames.clear();
078    if (ct == null) {
079      error(x,"No Content Type");
080    } else if (ct.startsWith("image/")) {
081      image(x, id, ct, cnt);
082    } else if (isXml(ct)) {
083      xml(x, cnt);
084    } else if (isJson(ct)) {
085      json(x, cnt);      
086    } else if (isTtl(ct)) {
087      ttl(x, cnt);      
088    } else if (isText(ct)) {
089      text(x, cnt, codeForMimeType(ct));      
090    } else {
091      error(x, "The Content Type '"+ct+"' is not rendered in this context");
092    }
093  }
094
095
096  private String codeForMimeType(String ct) {
097    switch (ct) {
098    case "text/x-gherkin": return "gherkin language-gherkin";
099    }
100    return scriptMappings.get(ct);
101  }
102
103  private void image(XhtmlNode x, String id, String ct, byte[] cnt) throws IOException {
104    String ext = null;
105    if (ct.startsWith("image/png")) {
106      ext = ".png";
107    } else if (ct.startsWith("image/jpeg")) {
108      ext = ".jpg";
109    } else if (ct.startsWith("image/gif")) {
110      ext = ".gif";
111    } else if (ct.startsWith("image/svg")) {
112      ext = ".svg";
113    }
114    
115    if (ext == null) {
116      error(x, "The Image Type '"+ct+"' is not rendered in this context");
117    } else {
118      String fn = "Binary-Native-"+id+ext;
119      FileUtilities.bytesToFile(cnt, Utilities.path(folder, fn));
120      filenames.add(fn);
121      x.img("Binary-Native-"+id+ext, "binary");
122    }
123  }
124
125  private void error(XhtmlNode x, String message) {
126    x.tx("["+message+"]");
127  }
128
129  private boolean isXml(String ct) {
130    return ct.startsWith("text/xml") || ct.startsWith("application/xml") || ct.contains("+xml");
131  }
132
133  private void xml(XhtmlNode x, byte[] cnt) {
134    String content = "\r\n"+getBinContentAsString(cnt);
135    XhtmlNode pre = x.pre("xml");
136    pre.code(content);    
137  }
138  
139  private boolean isJson(String ct) {
140    return ct.startsWith("text/json") || ct.startsWith("application/json") || ct.contains("+json");
141  }
142  
143  private void json(XhtmlNode x, byte[] cnt) {
144    String content = "\r\n"+getBinContentAsString(cnt);
145    XhtmlNode pre = x.pre("json");
146    pre.code(content);    
147  }
148  
149  private boolean isTtl(String ct) {
150    return ct.startsWith("text/rdf") || ct.contains("+turtle");
151  }
152  
153  private void ttl(XhtmlNode x, byte[] cnt) {
154    String content = "\r\n"+getBinContentAsString(cnt);
155    XhtmlNode pre = x.pre("rdf language-turtle");
156    pre.code(content);    
157  }
158  
159
160  private boolean isText(String ct) {
161    return ct.startsWith("text/");
162  }
163  
164  private void text(XhtmlNode x, byte[] cnt, String code) {
165    String content = "\r\n"+getBinContentAsString(cnt);
166    XhtmlNode pre = x.pre(code);
167    pre.code(content);    
168  }
169  
170
171}