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: <pre class="language-java"> 050 * <code> 051 * @JStacheFormatter 052 * @JStacheFormatterTypes(types={LocalDate.class}) 053 * public class MyFormatter { 054 * //required factory method 055 * public static Formatter provider() { ... } 056 * } 057 * </code></pre> 058 * 059 * <p> 060 * <strong>All formatters should be able to handle:</strong> 061 * <ul> 062 * <li>{@link String} 063 * <li>native types both unboxed or boxed 064 * <li>{@link URI} 065 * <li>{@link URL} 066 * </ul> 067 * 068 * <em>(whether they output something however is up to the formatter).</em> Because the 069 * allowed types can be widened more than what the formatter is annotated for a formatter 070 * also needs to be prepared for that. The default formatters in JStachio will 071 * <code>toString()</code> objects that are not recognized but whose types are whitelisted 072 * via {@link JStacheFormatterTypes} from other config. 073 * 074 * <p> 075 * Because formatters have to be rather omiscient of all types consider using a lambda 076 * {@link JStacheLambda} for formatting complex objects. 077 * 078 * @apiNote <em> n.b. the class annotated does not need to implement the formatter 079 * interface! It just needs to provide it.</em> 080 * 081 * @author agentgt 082 * @see JStacheFormatterTypes 083 * @see JStacheConfig#formatter() 084 * 085 */ 086@Retention(RetentionPolicy.RUNTIME) 087@Target(ElementType.TYPE) 088@Documented 089public @interface JStacheFormatter { 090 091 // TODO maybe this should be called JStacheFormatterProvider? 092 093 /** 094 * A static method that will return an implementation of 095 * {@code io.jstach.api.runtime.Formatter} or {@code Function<Object,String> } 096 * @return default method name is <code>provider</code> just like the 097 * {@link ServiceLoader} 098 */ 099 String providesMethod() default "provider"; 100 101 /** 102 * A formatter type marker to resolve the formatter based on config elsewhere. 103 * 104 * @apiNote The <code>provider</code> method is purposely missing to avoid coupling 105 * with the runtime. 106 * @author agentgt 107 */ 108 @JStacheFormatter 109 public final class UnspecifiedFormatter { 110 111 private UnspecifiedFormatter() { 112 } 113 114 } 115 116}