001package io.jstach.jstachio; 002 003import java.io.IOException; 004import java.util.ServiceLoader; 005import java.util.function.Supplier; 006 007import io.jstach.jstache.JStache; 008import io.jstach.jstachio.spi.JStachioExtension; 009 010/** 011 * Render models by using reflection to lookup generated templates as well as apply 012 * filtering and fallback mechanisms. 013 * <h2>Example Usage</h2> <pre><code class="language-java"> 014 * @JStache(template = "Hello {{name}}!") 015 * public record HelloWorld(String name) {} 016 * 017 * public static String output(String name) { 018 * //Normally you would have to use generated class HelloWorldRenderer 019 * //but this JStachio allows you to render directly 020 * //from the model. 021 * return JStachio.render(new HelloWorld(name)); 022 * } 023 * </code> </pre> Not only is the above more convenient than using the raw generated code 024 * it also allows additional custom runtime behavior like filtering as well as allows 025 * easier integration with web frameworks. 026 * 027 * @apiNote The static <strong><code>render</code></strong> methods are convenience 028 * methods that will use the ServiceLoader based JStachio which loads all extensions via 029 * the {@link ServiceLoader}. 030 * @see JStachioExtension 031 * @see JStache 032 */ 033public interface JStachio extends Renderer<Object> { 034 035 /** 036 * Finds a template by using the models class if possible and then applies filtering 037 * and then finally render the model by writing to the appendable. 038 * <p> 039 * {@inheritDoc} 040 */ 041 void execute(Object model, Appendable appendable) throws IOException; 042 043 /** 044 * Finds a template by using the models class if possible and then applies filtering 045 * and then finally render the model by writing to the {@link StringBuilder}. 046 * <p> 047 * {@inheritDoc} 048 */ 049 StringBuilder execute(Object model, StringBuilder sb); 050 051 /** 052 * Finds a template by using the models class if possible and then applies filtering 053 * and then finally render the model to a String. 054 * <p> 055 * {@inheritDoc} 056 */ 057 String execute(Object model); 058 059 /** 060 * Determines if this jstachio can render the model type (the class annotated by 061 * JStache). 062 * @param modelType the models class (<em>the one annotated with {@link JStache} and 063 * not the Templates class</em>) 064 * @return true if this jstachio can render instances of modelType 065 */ 066 boolean supportsType(Class<?> modelType); 067 068 /** 069 * Executes the ServiceLoader instance of JStachio 070 * {@link #execute(Object, Appendable)}. 071 * @param model never <code>null</code> 072 * @param a appendable never <code>null</code> 073 * @throws IOException if there is an error using the appendable 074 * @see #execute(Object, Appendable) 075 */ 076 public static void render(Object model, Appendable a) throws IOException { 077 of().execute(model, a); 078 } 079 080 /** 081 * Executes the ServiceLoader instance of JStachio 082 * {@link #execute(Object, StringBuilder)}. 083 * @param model never <code>null</code> 084 * @param a appendable never <code>null</code> 085 * @return the passed in {@link StringBuilder} 086 * @see #execute(Object, StringBuilder) 087 */ 088 public static StringBuilder render(Object model, StringBuilder a) { 089 return of().execute(model, a); 090 } 091 092 /** 093 * Executes the ServiceLoader instance of JStachio {@link #execute(Object)}. 094 * @param model the root context model. Never <code>null</code>. 095 * @return the rendered string. 096 * @see #execute(Object) 097 */ 098 public static String render(Object model) { 099 return of().execute(model); 100 } 101 102 /** 103 * Gets the static singleton jstachio. 104 * @return the jstachio from {@link #setStaticJStachio(Supplier)} 105 * @throws NullPointerException if jstachio is not found 106 * @see #setStaticJStachio(Supplier) 107 */ 108 public static JStachio of() { 109 JStachio jstachio = JStachioHolder.get(); 110 if (jstachio == null) { 111 throw new NullPointerException("JStachio not found. This is probably a classloading issue."); 112 } 113 return jstachio; 114 } 115 116 /** 117 * Gets default singleton ServiceLoader based jstachio. 118 * @return service loaded jstachio 119 */ 120 public static JStachio defaults() { 121 return io.jstach.jstachio.spi.JStachioFactory.defaultJStachio(); 122 } 123 124 /** 125 * Set the static singleton of JStachio. 126 * <p> 127 * Useful if you would like to avoid using the default ServiceLoader mechanism. 128 * @param jstachioProvider if null a NPE will be thrown. 129 * @apiNote the provider will be called on every call of {@link #of()} and thus to 130 * avoid constant recreation it is recommend the supplier be memoized/cached. 131 */ 132 public static void setStaticJStachio(Supplier<JStachio> jstachioProvider) { 133 if (jstachioProvider == null) { 134 throw new NullPointerException("JStachio provider cannot be null"); 135 } 136 JStachioHolder.provider = jstachioProvider; 137 } 138 139} 140 141final class JStachioHolder { 142 143 static Supplier<JStachio> provider = JStachio::defaults; 144 145 static JStachio get() { 146 return provider.get(); 147 } 148 149}