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.model.Reference;
013import org.hl7.fhir.r5.model.Resource;
014import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
015import org.hl7.fhir.r5.renderers.utils.RenderingContext;
016import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
017import org.hl7.fhir.r5.utils.EOperationOutcome;
018import org.hl7.fhir.r5.utils.LiquidEngine;
019import org.hl7.fhir.r5.utils.LiquidEngine.ILiquidRenderingSupport;
020import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument;
021import org.hl7.fhir.utilities.xhtml.NodeType;
022import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
023import org.hl7.fhir.utilities.xhtml.XhtmlNode;
024import org.hl7.fhir.utilities.xhtml.XhtmlParser;
025
026public class LiquidRenderer extends ResourceRenderer implements ILiquidRenderingSupport {
027
028  public class LiquidRendererContxt {
029
030    private ResourceContext rcontext;
031    private ResourceWrapper resource;
032
033    public LiquidRendererContxt(ResourceContext rcontext, ResourceWrapper r) {
034      this.rcontext = rcontext;
035      this.resource = r;
036    }
037
038    public ResourceWrapper getResource() {
039      return resource;
040    }
041
042  }
043
044  private String liquidTemplate;
045
046  public LiquidRenderer(RenderingContext context, String liquidTemplate) {
047    super(context);
048    this.liquidTemplate = liquidTemplate;
049  }
050
051  public LiquidRenderer(RenderingContext context, ResourceContext rcontext, String liquidTemplate) {
052    super(context);
053    this.rcontext = rcontext;
054    this.liquidTemplate = liquidTemplate;
055  }
056
057  /**
058   * This class provides an implementation of the ILiquidEngineIncludeResolver that makes use of the
059   * template provider available in the rendering context to support resolving includes.
060   */
061  private class LiquidRendererIncludeResolver implements LiquidEngine.ILiquidEngineIncludeResolver {
062    public LiquidRendererIncludeResolver(RenderingContext context) {
063      this.context = context;
064    }
065
066    private RenderingContext context;
067
068    @Override
069    public String fetchInclude(LiquidEngine engine, String name) {
070      return context.getTemplateProvider().findTemplate(context, name);
071    }
072  }
073  
074  @Override
075  public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
076    LiquidEngine engine = new LiquidEngine(context.getWorker(), context.getServices());
077    XhtmlNode xn;
078    try {
079      engine.setIncludeResolver(new LiquidRendererIncludeResolver(context));
080      engine.setRenderingSupport(this);
081      LiquidDocument doc = engine.parse(liquidTemplate, "template");
082      String html = engine.evaluate(doc, r, rcontext);
083      xn = new XhtmlParser().parseFragment(html);
084      if (!x.getName().equals("div"))
085        throw new FHIRException("Error in template: Root element is not 'div'");
086    } catch (FHIRException | IOException e) {
087      xn = new XhtmlNode(NodeType.Element, "div");
088      xn.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
089    }
090    x.getChildNodes().addAll(xn.getChildNodes());
091    return true;
092  }
093
094  @Override
095  public String display(Resource r) throws UnsupportedEncodingException, IOException {
096    return "not done yet";
097  }
098
099  public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
100    if (r.has("title")) {
101      return r.children("title").get(0).getBase().primitiveValue();
102    }
103    if (r.has("name")) {
104      return r.children("name").get(0).getBase().primitiveValue();
105    }
106    return "??";
107  }
108
109  @Override
110  public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
111    LiquidEngine engine = new LiquidEngine(context.getWorker(), context.getServices());
112    XhtmlNode xn;
113    try {
114      engine.setIncludeResolver(new LiquidRendererIncludeResolver(context));
115      LiquidDocument doc = engine.parse(liquidTemplate, "template");
116      engine.setRenderingSupport(this);
117      String html = engine.evaluate(doc, r.getBase(), new LiquidRendererContxt(rcontext, r));
118      xn = new XhtmlParser().parseFragment(html);
119      if (!x.getName().equals("div"))
120        throw new FHIRException("Error in template: Root element is not 'div'");
121    } catch (FHIRException | IOException e) {
122      xn = new XhtmlNode(NodeType.Element, "div");
123      xn.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
124    }
125    x.getChildNodes().addAll(xn.getChildNodes());
126    return true;
127  }
128
129  public RendererType getRendererType() {
130    return RendererType.LIQUID;
131  }
132
133  @Override
134  public String renderForLiquid(Object appContext, Base base) throws FHIRException {
135    try {
136      if (base instanceof Element) {
137        base = context.getParser().parseType((Element) base);
138      }
139      XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
140      if (base instanceof Reference) {
141        renderReference(((LiquidRendererContxt) appContext).getResource(), x, (Reference) base);        
142      } else if (base instanceof DataType) {
143        render(x, (DataType) base);
144      } else {
145        x.tx(base.toString());
146      }
147      String res = new XhtmlComposer(true).compose(x);
148      res = res.substring(5);
149      if (res.length() < 6) {
150        return "";
151      } else {
152        return res.substring(0, res.length()-6);
153      }
154    } catch (FHIRFormatError e) {
155      throw new FHIRException(e);
156    } catch (IOException e) {
157      throw new FHIRException(e);
158    }
159  }
160
161}