001package io.jstach.jstache;
002
003import java.lang.annotation.Annotation;
004import java.lang.annotation.Documented;
005import java.lang.annotation.ElementType;
006import java.lang.annotation.Inherited;
007import java.lang.annotation.Retention;
008import java.lang.annotation.RetentionPolicy;
009import java.lang.annotation.Target;
010
011/**
012 * Compiler <strong>feature flags that are subject to change</strong>. Use at your own
013 * risk!
014 * <p>
015 * <strong>Flags maybe added without a major version change unlike the rest of the
016 * API.</strong> If a flag becomes popular enough it will eventually make its way to
017 * {@link JStacheConfig} so please file an issue if you depend on flag and would like it
018 * to remain in the library.
019 * <p>
020 * Order of flag lookup and precedence is as follows:
021 * <ol>
022 * <li>type annotated with JStache and this annotation.
023 * <li>enclosing class (of type annotated with JStache) with this annotation with inner to
024 * outer order.
025 * <li>package annotated with this annotation.
026 * <li>module annotated with this annotation.
027 * <li>annotation processor compiler arg options (<code>-A</code>). The flags are
028 * lowercased and prefixed with "<code>jstache.</code>"</li>
029 * </ol>
030 * <em>The {@link #flags()} are NOT combined but rather the first found that is
031 * <strong>NOT</strong> containing {@link Flag#UNSPECIFIED} dictates the flags set or not
032 * (including empty)</em>. If other flags are set with UNSPECIFIED they will be ignored.
033 *
034 * @author agentgt
035 * @apiNote the retention policy is purposely {@link RetentionPolicy#SOURCE} as these
036 * flags only impact compiling of the template.
037 */
038@Retention(RetentionPolicy.SOURCE)
039@Target({ ElementType.MODULE, ElementType.PACKAGE, ElementType.TYPE })
040@Documented
041public @interface JStacheFlags {
042
043        /**
044         * Compiler flags that will be used on for this model. <em>The {@link #flags()} are
045         * NOT combined but rather the first found that is <strong>NOT</strong> containing
046         * {@link Flag#UNSPECIFIED} dictates the flags set or not (including empty)</em>. If
047         * other flags are set with UNSPECIFIED they will be ignored.
048         * @return flags defaults to a single unspecified.
049         * @see JStacheFlags
050         */
051        Flag[] flags() default { Flag.UNSPECIFIED };
052
053        /**
054         * Compiler flags. Besides setting with {@link JStacheFlags} the flags are also
055         * available as annotation processor options but are prefixed with
056         * "<code>jstache.</code>" and lowercased
057         * <p>
058         * For example {@link Flag#DEBUG} would be: <code>-Ajstache.debug=true/false</code>
059         *
060         * @apiNote SUBJECT TO CHANGE!
061         * @author agentgt
062         *
063         */
064        public enum Flag {
065
066                /**
067                 * Flag to indicate nothing is set. This is to differentiate a request to unset
068                 * {@link JStacheFlags#flags()} flags vs ignore and cascade up. See
069                 * {@link JStacheConfig} for config cascading.
070                 * @see JStacheConfig
071                 */
072                UNSPECIFIED,
073
074                /**
075                 * This will produce additional logging that is sent to standard out while the
076                 * annotation processor runs (not during runtime).
077                 */
078                DEBUG,
079                /**
080                 * Per mustache spec dotted names can actually not exist at all for inverted
081                 * sections. This flag disables that so that a compiler failure will happen if the
082                 * fields are missing.
083                 *
084                 * For example assume "missing" is not present on "data" as in data has no field
085                 * or method called "missing".
086                 *
087                 * <pre>
088                 * {{^data.missing}}
089                 * {{/data.missing}}
090                 * </pre>
091                 *
092                 * Normally the above will compile just fine per the spec but this can lead to
093                 * bugs. To not allow what the spec calls "dotted broken chains" you can use this
094                 * flag.
095                 */
096                NO_INVERTED_BROKEN_CHAIN,
097
098                /**
099                 * <strong>EXPERIMENTAL:</strong> Normally falsey is either empty list, boolean
100                 * false, or <code>null</code>. This flag disables <code>null</code> as a falsey
101                 * check.
102                 *
103                 * For example when opening a section like: <pre><code class="language-hbs">
104                 * {{#myNonNull}}
105                 * Hi!
106                 * {{/myNonNull}}
107                 * </code> </pre>
108                 *
109                 * JStachio would produce code that checks if <code>myNonNull</code> is null as
110                 * well as iterate if it is a list or check if true if it is a boolean.
111                 *
112                 * <p>
113                 * However null checking will still be done if JStachio can find a
114                 * {@link ElementType#TYPE_USE} annotation with the {@link Class#getSimpleName()}
115                 * of <code>Nullable</code> on the type that is being accessed as a section. This
116                 * follows <a href="https://github.com/jspecify/jspecify">JSpecify rules</a> but
117                 * not other nullable annotations like
118                 * <a href="https://spotbugs.github.io/">SpotBugs</a> that are not
119                 * {@link ElementType#TYPE_USE}.
120                 *
121                 * <h4>Benefits</h4>
122                 *
123                 * The advantages of disabling null checking are:
124                 * <ul>
125                 * <li>Failing fast instead of just not rendering something which may make finding
126                 * bugs easier.</li>
127                 * <li>Less generated code which maybe easier to read</li>
128                 * <li>Avoid warnings of superfluous null checking by static analysis tools</li>
129                 * <li>Possible slight improvement of performance</li>
130                 * </ul>
131                 *
132                 * <h4>Caveats</h4>
133                 *
134                 * <h5>JDK Bug</h5> Because of JDK bug:
135                 * <a href="https://bugs.openjdk.org/browse/JDK-8225377">JDK-8225377</a> this
136                 * <em>nullable detection will only work if the type that is being checked is
137                 * currently within the same compile boundary as the JStache model being
138                 * analyzed!</em>
139                 *
140                 * <h5>Manually checking for null</h5> If JStachio cannot detect that the type is
141                 * nullable because it is not annotated or because of the aforementioned JDK bug
142                 * then it will conclude that it can never be null and thus you will be unable to
143                 * use section like conditions to check if is null. One workaround is to use a
144                 * custom {@link JStacheLambda} to check for null.
145                 *
146                 * @apiNote This is currently experimental and a flag because of the JDK bug. In
147                 * the future more comprehensive support will be put in {@link JStacheConfig}.
148                 */
149                NO_NULL_CHECKING,
150
151                /**
152                 * If set the templates will <strong>NOT</strong> have pre-encoded bytes of the
153                 * static parts of the template and the generated {@link JStacheType#JSTACHIO}
154                 * code will not implement
155                 * <code>io.jstach.jstachio.Template.EncodedTemplate</code>.
156                 */
157                PRE_ENCODE_DISABLE;
158
159        }
160
161        /**
162         * <strong>EXPERIMENTAL:</strong> Annotation to use for marking nullable types in
163         * generated code. Normally JStachio will just put a comment like
164         * "<code>&#47;* &#64;Nullable *&#47;</code>" for allowed nulls.
165         * <p>
166         * For example by default a formatter that accepts nulls will be generated like: <pre>
167         * <code class="language-java">
168         * Function&lt;&#47;* &#64;Nullable *&#47; Object, String&gt; formatter;
169         * </code> </pre> If this feature is turned on then the comment will be replaced with
170         * the given annotation.
171         * <p>
172         * The annotation must be a {@link ElementType#TYPE_USE} compatible annotation. Also
173         * known as JSR 308. Most JSR 305 style annotations will not work!
174         * <p>
175         * The default return annotation of Inherited has no special meaning other than
176         * UNSPECIFIED and will not actually be used. It was chosen arbitrarily because there
177         * are currently no annotations in java.base that correlate with this behavior.
178         * @return {@link Inherited} signaling unspecified (it will not actually be used).
179         * @apiNote The annotation other than the unspecified must be a
180         * {@link ElementType#TYPE_USE}.
181         */
182        Class<? extends Annotation> nullableAnnotation() default Inherited.class;
183
184}