001package io.jstach.jstachio.context; 002 003import io.jstach.jstache.JStacheConfig; 004import io.jstach.jstachio.Output; 005import io.jstach.jstachio.Output.EncodedOutput; 006import io.jstach.jstachio.Template; 007 008/** 009 * A context aware template. 010 * 011 * @param <T> model type 012 */ 013public interface ContextTemplate<T> { 014 015 /** 016 * Renders the passed in model to an appendable like output. 017 * @param <A> output type 018 * @param <E> error type 019 * @param model a model assumed never to be <code>null</code>. 020 * @param context context node. 021 * @param appendable the appendable to write to. 022 * @return the passed in appendable for convenience 023 * @throws E if there is an error writing to the output 024 * @apiNote if the eventual output is to be bytes use 025 * {@link #write(Object, ContextNode, io.jstach.jstachio.Output.EncodedOutput)} as it 026 * will leverage pre-encoding if the template has it. 027 */ 028 public <A extends Output<E>, E extends Exception> A execute( // 029 T model, // 030 ContextNode context, // 031 A appendable) throws E; 032 033 /** 034 * Renders the passed in model directly to a binary stream leveraging pre-encoded 035 * parts of the template. This <em>may</em> improve performance when rendering UTF-8 036 * to an OutputStream as some of the encoding is done in advance. Because the encoding 037 * is done statically you cannot pass the charset in. The chosen charset comes from 038 * {@link JStacheConfig#charset()}. 039 * @param <A> output type 040 * @param <E> error type 041 * @param model a model assumed never to be <code>null</code>. 042 * @param context context node. 043 * @param output to write to. 044 * @return the passed in output for convenience 045 * @throws E if an error occurs while writing to output 046 */ 047 public <A extends io.jstach.jstachio.Output.EncodedOutput<E>, E extends Exception> A write( // 048 T model, // 049 ContextNode context, // 050 A output) throws E; 051 052 /** 053 * Creates a context template from a regular template if is not already a context 054 * template. 055 * @param <T> model type 056 * @param template the template to be wrapped 057 * @return context template 058 */ 059 @SuppressWarnings({ "null", "unchecked" }) 060 public static <T> ContextTemplate<T> of(Template<T> template) { 061 if (template instanceof ContextTemplate ct) { 062 return ct; 063 } 064 return new DecoratedContextTemplate<>(template); 065 } 066 067} 068 069record DecoratedContextTemplate<T> (Template<T> template) implements ContextTemplate<T> { 070 071 @Override 072 public <A extends Output<E>, E extends Exception> A execute(T model, ContextNode context, A appendable) throws E { 073 var out = ContextAwareOutput.of(appendable, context); 074 template.execute(model, out); 075 return appendable; 076 } 077 078 @Override 079 public <A extends EncodedOutput<E>, E extends Exception> A write(T model, ContextNode context, A output) throws E { 080 var out = ContextAwareOutput.of(output, context); 081 template.write(model, out); 082 return output; 083 } 084 085}