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