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}