001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005 006import org.hl7.fhir.exceptions.DefinitionException; 007import org.hl7.fhir.exceptions.FHIRException; 008import org.hl7.fhir.exceptions.FHIRFormatError; 009import org.hl7.fhir.r5.elementmodel.Element; 010import org.hl7.fhir.r5.model.Base; 011import org.hl7.fhir.r5.model.DataType; 012import org.hl7.fhir.r5.renderers.utils.RenderingContext; 013import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 014import org.hl7.fhir.r5.utils.EOperationOutcome; 015import org.hl7.fhir.r5.utils.LiquidEngine; 016import org.hl7.fhir.r5.utils.LiquidEngine.ILiquidRenderingSupport; 017import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument; 018import org.hl7.fhir.utilities.xhtml.NodeType; 019import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 020import org.hl7.fhir.utilities.xhtml.XhtmlNode; 021import org.hl7.fhir.utilities.xhtml.XhtmlParser; 022 023public class LiquidRenderer extends ResourceRenderer implements ILiquidRenderingSupport { 024 025 private String liquidTemplate; 026 027 private class LiquidRendererContext { 028 private RenderingStatus status; 029 private ResourceWrapper resource; 030 protected LiquidRendererContext(RenderingStatus status, ResourceWrapper resource) { 031 super(); 032 this.status = status; 033 this.resource = resource; 034 } 035 036 } 037 038 public LiquidRenderer(RenderingContext context, String liquidTemplate) { 039 super(context); 040 this.liquidTemplate = liquidTemplate; 041 } 042 043 @Override 044 public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 045 return canonicalTitle(r); 046 } 047 048 /** 049 * This class provides an implementation of the ILiquidEngineIncludeResolver that makes use of the 050 * template provider available in the rendering context to support resolving includes. 051 */ 052 private class LiquidRendererIncludeResolver implements LiquidEngine.ILiquidEngineIncludeResolver { 053 public LiquidRendererIncludeResolver(RenderingContext context) { 054 this.context = context; 055 } 056 057 private RenderingContext context; 058 059 @Override 060 public String fetchInclude(LiquidEngine engine, String name) { 061 return context.getTemplateProvider().findTemplate(context, name); 062 } 063 } 064 065 @Override 066 public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { 067 LiquidEngine engine = new LiquidEngine(context.getWorker(), context.getServices()); 068 XhtmlNode xn; 069 try { 070 engine.setIncludeResolver(new LiquidRendererIncludeResolver(context)); 071 engine.setRenderingSupport(this); 072 LiquidDocument doc = engine.parse(liquidTemplate, "template"); 073 String html = engine.evaluate(doc, r.getBase(), new LiquidRendererContext(status, r)); 074 xn = new XhtmlParser().parseFragment(html); 075 if (!x.getName().equals("div")) 076 throw new FHIRException("Error in template: Root element is not 'div'"); 077 } catch (FHIRException | IOException e) { 078 xn = new XhtmlNode(NodeType.Element, "div"); 079 xn.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage()); 080 } 081 x.addChildNodes(xn.getChildNodes()); 082 status.setExtensions(true); 083 } 084 085 public RendererType getRendererType() { 086 return RendererType.LIQUID; 087 } 088 089 @Override 090 public String renderForLiquid(Object appContext, Base base) throws FHIRException { 091 try { 092 LiquidRendererContext ctxt = (LiquidRendererContext) appContext; 093 ResourceWrapper r = null; 094 if (base instanceof Element) { 095 r = ResourceWrapper.forType(context.getContextUtilities(), (Element) base); 096 } else if (base instanceof DataType) { 097 r = ResourceWrapper.forType(context.getContextUtilities(), (DataType) base); 098 } else { 099 return base.toString(); 100 } 101 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 102 renderDataType(ctxt.status, x, r); 103 String res = new XhtmlComposer(true).compose(x); 104 res = res.substring(5); 105 if (res.length() < 6) { 106 return ""; 107 } else { 108 return res.substring(0, res.length()-6); 109 } 110 } catch (FHIRFormatError e) { 111 throw new FHIRException(e); 112 } catch (IOException e) { 113 throw new FHIRException(e); 114 } 115 } 116 117}