001package io.jstach.jstachio; 002 003import java.io.IOException; 004import java.io.OutputStream; 005 006import io.jstach.jstache.JStacheInterfaces; 007import io.jstach.jstachio.Output.EncodedOutput; 008import io.jstach.jstachio.Template.EncodedTemplate; 009 010/** 011 * A template and model combined with convenience methods. 012 * <p> 013 * This tuple like object is useful way to combine the correct template with a model if 014 * you have programmatic access to the template. This can be useful to a web framework 015 * perhaps as the return type of a web controller and because the template is already 016 * located the web framework does not have to re-find the template. An analog in the 017 * Spring framework would be <code>ModelAndView</code>. 018 * <p> 019 * It is purposely not parameterized for ease of use as dealing with generics via 020 * reflection can be difficult. 021 * 022 * @apiNote the interface is purposely sealed for now to see usage and feedback and one 023 * can make their own similar version with {@link JStacheInterfaces}. 024 * @author agentgt 025 * 026 */ 027public sealed interface TemplateModel { 028 029 /** 030 * Template. 031 * @return template to use for execution 032 */ 033 Template<?> template(); 034 035 /** 036 * Model. 037 * @return model to be executed on 038 */ 039 Object model(); 040 041 /** 042 * Renders the passed in model directly to an appendable like output. 043 * @param <A> output type 044 * @param <E> error type 045 * @param output to write to. 046 * @return the passed in output 047 * @throws E if an error occurs while writing to output 048 */ 049 public <A extends io.jstach.jstachio.Output<E>, E extends Exception> A execute(A output) throws E; 050 051 /** 052 * Renders the passed in model directly to a binary stream possibly leveraging 053 * pre-encoded parts of the template. 054 * @param <A> output type 055 * @param <E> error type 056 * @param output to write to. 057 * @return the passed in output 058 * @throws E if an error occurs while writing to output 059 */ 060 default <A extends io.jstach.jstachio.Output.EncodedOutput<E>, E extends Exception> A write(A output) throws E { 061 return execute(output); 062 } 063 064 /** 065 * Convenience method to write directly to an outputstream with the templates 066 * character encoding. 067 * @param outputStream outputStream to write to 068 * @throws IOException if an error happens while writting to the stream. 069 */ 070 default void write(OutputStream outputStream) throws IOException { 071 write(Output.of(outputStream, template().templateCharset())); 072 } 073 074 /** 075 * Renders the template to a String. 076 * @return the executed template as a string. 077 */ 078 default String execute() { 079 return execute(Output.of(new StringBuilder())).getBuffer().toString(); 080 } 081 082 /** 083 * Creates a template model pair. 084 * @param <T> model type 085 * @param template encoded template 086 * @param model model instance 087 * @return the template executable designed for encoded templates. 088 */ 089 public static <T> TemplateModel of(EncodedTemplate<T> template, T model) { 090 return new EncodedTemplateExecutable<>(template, model); 091 } 092 093 /** 094 * Creates a template model pair. 095 * @param <T> model type 096 * @param template encoded template 097 * @param model model instance 098 * @return the template executable. 099 */ 100 public static <T> TemplateModel of(Template<T> template, T model) { 101 if (template instanceof EncodedTemplate<T> et) { 102 return of(et, model); 103 } 104 return new DefaultTemplateExecutable<>(template, model); 105 } 106 107} 108 109record DefaultTemplateExecutable<T> (Template<T> delegateTemplate, T _model) implements TemplateModel { 110 111 static final String ERROR_MESSAGE = "The model passed into this TemplateModel is not correct"; 112 113 @Override 114 public <A extends io.jstach.jstachio.Output<E>, E extends Exception> A execute(A output) throws E { 115 return delegateTemplate.execute(this._model, output); 116 } 117 118 @Override 119 public Template<?> template() { 120 return delegateTemplate; 121 } 122 123 @Override 124 public Object model() { 125 var m = _model; 126 // Both eclipse and checkerframework seem to be unable to apply nonnull 127 // to records with parameters 128 if (m == null) { 129 throw new NullPointerException("model is null"); 130 } 131 return m; 132 } 133 134} 135 136record EncodedTemplateExecutable<T> (EncodedTemplate<T> delegateTemplate, T _model) implements TemplateModel { 137 138 @Override 139 public <A extends io.jstach.jstachio.Output<E>, E extends Exception> A execute(A output) throws E { 140 return delegateTemplate.execute(this._model, output); 141 } 142 143 @Override 144 public <A extends EncodedOutput<E>, E extends Exception> A write(A output) throws E { 145 return delegateTemplate.write(_model, output); 146 } 147 148 @Override 149 public Template<?> template() { 150 return delegateTemplate; 151 } 152 153 @Override 154 public Object model() { 155 var m = _model; 156 // Both eclipse and checkerframework seem to be unable to apply nonnull 157 // to records with parameters 158 if (m == null) { 159 throw new NullPointerException("model is null"); 160 } 161 return m; 162 } 163 164}