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 * &#64;JStacheFormatter
052 * &#64;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}