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