001package io.jstach.jstache; 002 003import java.lang.annotation.Documented; 004import java.lang.annotation.ElementType; 005import java.lang.annotation.Retention; 006import java.lang.annotation.RetentionPolicy; 007import java.lang.annotation.Target; 008import java.net.URI; 009import java.net.URL; 010import java.util.ServiceLoader; 011 012/** 013 * Statically registers a formatter. 014 * <p> 015 * Formatters are called before escapers to resolve the output of a variable (e.g. 016 * {{variable}}). Colloquially you can think of them as glorified 017 * {@link Object#toString()} on objects. 018 * <p> 019 * A class that is annotated statically provides a formatter instead of using the 020 * {@link ServiceLoader} and can be used as marker for a particular formatter in 021 * {@link JStacheConfig#formatter()}. 022 * <p> 023 * There are two supported Formatter types: 024 * <ul> 025 * <li>{@code io.jstach.jstachio.Formatter} 026 * <li>{@code java.util.function.Function<Object,String>} 027 * </ul> 028 * 029 * The Function one is desirable if you would like no reference of jstachio runtime api in 030 * your code base and or just an easier interface to implement. 031 * <p> 032 * On the otherhand the Formatter interfaces allows potentially greater performance and or 033 * if you need to format native types. 034 * <p> 035 * It is important to understand that formatters do not have complete control what types 036 * are allowed to be formatted. <strong>That is a formatter might be able to output a 037 * certain class but the annotation processor will fail before that</strong> as only 038 * certain types are allowed to be formatted. To control what types are allowed to be 039 * formatted (and thus will use the formatter at runtime) see 040 * {@link JStacheFormatterTypes} which also allows the formatter itself to be annotated. 041 * <p> 042 * Consequently if a class is annotated with <code>this</code> annotation and 043 * {@link JStacheFormatterTypes} the types will be added to the whitelist of allowed types 044 * if the annotated formatter is picked. Because you often need to whitelist types to 045 * allow customer formatters it is a best practice you annotate the formatter with 046 * whatever custom types are allowed. 047 * <p> 048 * For example let us say we want to add a custom formatter that is aware of 049 * <code>LocalDate</code> we would add: 050 * 051 * <pre class="language-java"> 052 * <code> 053 * @JStacheFormatter 054 * @JStacheFormatterTypes(types={LocalDate.class}) 055 * public class MyFormatter { 056 * //required factory method 057 * public static Formatter provider() { ... } 058 * } 059 * </code></pre> 060 * 061 * Or for zero dependency: 062 * 063 * <pre class="language-java"> 064 * <code> 065 * @JStacheFormatter 066 * @JStacheFormatterTypes(types={LocalDate.class}) 067 * public class MyFormatter { 068 * //required factory method 069 * public static Function<Object,String> provider() { ... } 070 * } 071 * </code></pre> 072 * 073 * <p> 074 * <strong>All formatters should be able to handle:</strong> 075 * <ul> 076 * <li>{@link String} 077 * <li>native types both unboxed or boxed 078 * <li>{@link URI} 079 * <li>{@link URL} 080 * </ul> 081 * 082 * <em>(whether they output something however is up to the formatter).</em> Because the 083 * allowed types can be widened more than what the formatter is annotated for a formatter 084 * also needs to be prepared for that. The default formatters in JStachio will 085 * <code>toString()</code> objects that are not recognized but whose types are whitelisted 086 * via {@link JStacheFormatterTypes} from other config. 087 * 088 * <p> 089 * Because formatters have to be rather omiscient of all types consider using a lambda 090 * {@link JStacheLambda} for formatting complex objects. 091 * 092 * @apiNote <em> n.b. the class annotated does not need to implement the formatter 093 * interface! It just needs to provide it.</em> 094 * 095 * @author agentgt 096 * @see JStacheFormatterTypes 097 * @see JStacheConfig#formatter() 098 * 099 */ 100@Retention(RetentionPolicy.RUNTIME) 101@Target(ElementType.TYPE) 102@Documented 103public @interface JStacheFormatter { 104 105 // TODO maybe this should be called JStacheFormatterProvider? 106 107 /** 108 * A static method that will return an implementation of 109 * {@code io.jstach.api.runtime.Formatter} or {@code Function<Object,String> } 110 * @return default method name is <code>provider</code> just like the 111 * {@link ServiceLoader} 112 */ 113 String providesMethod() default "provider"; 114 115 /** 116 * A formatter type marker to resolve the formatter based on config elsewhere. 117 * 118 * @apiNote The <code>provider</code> method is purposely missing to avoid coupling 119 * with the runtime. 120 * @author agentgt 121 */ 122 @JStacheFormatter 123 public final class UnspecifiedFormatter { 124 125 private UnspecifiedFormatter() { 126 } 127 128 } 129 130}