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 * Will always bind "@context" with a empty ContextNode. 161 */ 162 CONTEXT_SUPPORT_DISABLE; 163 164 } 165 166 /** 167 * <strong>EXPERIMENTAL:</strong> Annotation to use for marking nullable types in 168 * generated code. Normally JStachio will just put a comment like 169 * "<code>/* @Nullable */</code>" for allowed nulls. 170 * <p> 171 * For example by default a formatter that accepts nulls will be generated like: <pre> 172 * <code class="language-java"> 173 * Function</* @Nullable */ Object, String> formatter; 174 * </code> </pre> If this feature is turned on then the comment will be replaced with 175 * the given annotation. 176 * <p> 177 * The annotation must be a {@link ElementType#TYPE_USE} compatible annotation. Also 178 * known as JSR 308. Most JSR 305 style annotations will not work! 179 * <p> 180 * The default return annotation of Inherited has no special meaning other than 181 * UNSPECIFIED and will not actually be used. It was chosen arbitrarily because there 182 * are currently no annotations in java.base that correlate with this behavior. 183 * @return {@link Inherited} signaling unspecified (it will not actually be used). 184 * @apiNote The annotation other than the unspecified must be a 185 * {@link ElementType#TYPE_USE}. 186 */ 187 Class<? extends Annotation> nullableAnnotation() default Inherited.class; 188 189}