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      LiquidDocument doc = engine.parse(liquidTemplate, "template");
115      engine.setRenderingSupport(this);
116      String html = engine.evaluate(doc, r.getBase(), new LiquidRendererContxt(rcontext, r));
117      xn = new XhtmlParser().parseFragment(html);
118      if (!x.getName().equals("div"))
119        throw new FHIRException("Error in template: Root element is not 'div'");
120    } catch (FHIRException | IOException e) {
121      xn = new XhtmlNode(NodeType.Element, "div");
122      xn.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
123    }
124    x.getChildNodes().addAll(xn.getChildNodes());
125    return true;
126  }
127
128  public RendererType getRendererType() {
129    return RendererType.LIQUID;
130  }
131
132  @Override
133  public String renderForLiquid(Object appContext, Base base) throws FHIRException {
134    try {
135      if (base instanceof Element) {
136        base = context.getParser().parseType((Element) base);
137      }
138      XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
139      if (base instanceof Reference) {
140        renderReference(((LiquidRendererContxt) appContext).getResource(), x, (Reference) base);        
141      } else if (base instanceof DataType) {
142        render(x, (DataType) base);
143      } else {
144        x.tx(base.toString());
145      }
146      String res = new XhtmlComposer(true).compose(x).substring(5);
147      return res.substring(0, res.length()-6);
148    } catch (FHIRFormatError e) {
149      throw new FHIRException(e);
150    } catch (IOException e) {
151      throw new FHIRException(e);
152    }
153  }
154
155}