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 * &#64;JStacheFormatter
054 * &#64;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 * &#64;JStacheFormatter
066 * &#64;JStacheFormatterTypes(types={LocalDate.class})
067 * public class MyFormatter {
068 *   //required factory method
069 *   public static Function&lt;Object,String&gt; 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}