001package react4j.annotations;
002
003import arez.annotations.ActAsComponent;
004import arez.annotations.ArezComponent;
005import arez.annotations.Memoize;
006import arez.annotations.Observable;
007import arez.annotations.Observe;
008import arez.component.Identifiable;
009import java.lang.annotation.Documented;
010import java.lang.annotation.ElementType;
011import java.lang.annotation.Target;
012import javax.annotation.Nonnull;
013import react4j.Keyed;
014
015/**
016 * Annotation used to specify an abstract method that returns a prop.
017 * The property is extracted from Reacts underlying props object. By default the prop is passed as
018 * a value in when creating the view but it can also be retrieved from the react context.
019 *
020 * <p>The method that is annotated with this annotation must also comply with the following constraints:</p>
021 * <ul>
022 * <li>Must not be annotated with any other react annotation</li>
023 * <li>Must have 0 parameters</li>
024 * <li>Must return a value</li>
025 * <li>Must be an abstract instance method</li>
026 * <li>Must not throw exceptions</li>
027 * <li>Must be accessible from the same package as the class annotated by {@link View}</li>
028 * <li>
029 *   Should not be public as not expected to be invoked outside the view. A warning will be generated but can
030 *   be suppressed by the {@link SuppressWarnings} or {@link SuppressReact4jWarnings} annotations with a key
031 *   "React4j:PublicMethod". This warning is also suppressed by the annotation processor if it is implementing
032 *   an interface method.
033 * </li>
034 * <li>
035 *   Should not be protected if in the class annotated with the {@link View} annotation as the method is not
036 *   expected to be invoked outside the view. A warning will be generated but can be suppressed by the
037 *   {@link SuppressWarnings} or {@link SuppressReact4jWarnings} annotations with a key "React4j:ProtectedMethod".
038 * </li>
039 * <li>
040 *   Should be annotated with either {@link javax.annotation.Nonnull} or {@link javax.annotation.Nullable} if the
041 *   return type is not a primitive. A warning will be generated but can be suppressed by the {@link SuppressWarnings}
042 *   or {@link SuppressReact4jWarnings} annotations with a key "React4j:MissingInputNullability".
043 * </li>
044 * </ul>
045 */
046@Documented
047@Target( ElementType.METHOD )
048public @interface Input
049{
050  /**
051   * Return the name of the input.
052   * The name is the key used when accessing the input from the inputs object. It is also used when creating
053   * the builder steps associated with the inputs that set {@link #source()} to {@link Source#DEFAULT}.
054   *
055   * @return the name of the input.
056   */
057  @Nonnull
058  String name() default "<default>";
059
060  /**
061   * Return the qualifier used to access value from context.
062   * It must only be specified if {@link #source()} is set to {@link Source#CONTEXT}.
063   *
064   * @return the qualifier used to access value from context.
065   */
066  @Nonnull
067  String qualifier() default "";
068
069  /**
070   * The setting controlling where the input value is source from.
071   * If the source is set to {@link Source#CONTEXT} then the input is sometimes described as a "TreeInput"
072   * as it is transparently passed from a parent view to all child views. A "TreeInput" does not
073   * have to be specified by the user when creating the view.
074   *
075   * @return the setting controlling where the input value is source from.
076   */
077  Source source() default Source.DEFAULT;
078
079  /**
080   * Setting indicating whether the input should be supplied when the view is constructed.
081   * This influences validation when enabled and how the Builder class is created.
082   * If set to {@link Feature#ENABLE} then the user MUST supply the input and the builder will require the user
083   * to specify the value. If set to {@link Feature#DISABLE} then the user can optionally supply the input.
084   * If set to {@link Feature#AUTODETECT} then the annotation processor will treat it as {@link Feature#DISABLE}
085   * if there is a corresponding {@link InputDefault} for the input or the {@link #source()} parameter is set to
086   * {@link Source#CONTEXT}, otherwise it will be treated as {@link Feature#ENABLE}. The value of this setting
087   * must not be {@link Feature#ENABLE} when {@link #source()} is set to {@link Source#CONTEXT}.
088   *
089   * @return the flag indicating whether the input needs to be supplied.
090   */
091  Feature require() default Feature.AUTODETECT;
092
093  /**
094   * Indicate whether the input should be annotated by {@link Observable}.
095   *
096   * <p>If set to {@link Feature#AUTODETECT} then the input will be observable if and only if:</p>
097   * <ul>
098   * <li>{@link #immutable()} ()} is not set to {@code true}.</li>
099   * <li>the view has at least one method annotated with {@link Memoize} or {@link Observe}.</li>
100   * </ul>
101   *
102   * @return the enum indicating whether input is observable.
103   */
104  Feature observable() default Feature.AUTODETECT;
105
106  /**
107   * Return an enum indicating whether the view should check whether the value of the input is disposed
108   * prior to rendering. If the value is disposed then the render method will exit early and return null.
109   * If this parameter is set to {@link Feature#AUTODETECT} then the annotation processor will inspect the
110   * type of the input and treat it as {@link Feature#ENABLE} if the type is annotated with the {@link ArezComponent}
111   * annotation or the {@link ActAsComponent} annotation.
112   *
113   * @return an enum indicating whether the view should check whether the value of the input is disposed prior to rendering.
114   */
115  Feature disposable() default Feature.AUTODETECT;
116
117  /**
118   * Return an enum indicating whether the view should be disposed if the input is disposed. To enable this feature,
119   * the input MUST set {@link #immutable()} to <code>true</code>, {@link #disposable()} MUST resolve to
120   * {@link Feature#ENABLE}. The type of the input is expected to implement the {@link arez.component.DisposeNotifier}
121   * interface either directly or indirectly. If this parameter is set to {@link Feature#AUTODETECT} then the
122   * annotation processor will treat it as {@link Feature#ENABLE} if {@link #immutable()} is <code>true</code> and
123   * {@link #disposable()} resolves to {@link Feature#ENABLE}.
124   *
125   * @return an enum indicating whether the view should be disposed if the input is disposed.
126   */
127  Feature dependency() default Feature.AUTODETECT;
128
129  /**
130   * True if the input is not expected to change after initial value is set. If the value of the input does change
131   * then it is expected that the view will be unmounted and a new view created. This is implemented
132   * by synthesizing a key for the view every time the view that is derived from this input. To enable this
133   * the annotation processor must be able to identify the type of the input so that a key can be synthesized. The
134   * following types are supported by the annotation processor;
135   *
136   * <ul>
137   * <li>primitive types (i.e. boolean, short etc) and their corresponding boxed types (i.e. {@link java.lang.Boolean}, {@link java.lang.Short} etc).</li>
138   * <li>the {@link java.lang.String} type</li>
139   * <li>any class that implements {@link Keyed}</li>
140   * <li>any class that is annotated with {@link ArezComponent} where the {@link ArezComponent#requireId()} parameter does not resolve to {@link arez.annotations.Feature#DISABLE}</li>
141   * <li>any class or interface that is annotated with {@link ActAsComponent}. It is assumed that every implementation is an Arez component where the {@link ArezComponent#requireId()} parameter does not resolve to {@link arez.annotations.Feature#DISABLE}</li>
142   * <li>any class or interface that is compatible with {@link Identifiable}</li>
143   * <li>if none of the above scenarios is valid then the code will attempt to derive the key at runtime. First via the {@link Keyed} interface, then the {@link Identifiable} interface and if these strategies are not possible then toString() will be invoked on the input.</li>
144   * </ul>
145   *
146   * <p>It should be noted that if a type implements {@link Keyed} and is annotated with either {@link ArezComponent}
147   * or {@link ActAsComponent} then the annotation processor will assume the {@link Keyed} interface is to used in
148   * preference to other alternative strategies.</p>
149   *
150   * @return true if changing the input recreates the view.
151   */
152  boolean immutable() default false;
153
154  /**
155   * Enum where the input is sourced from.
156   */
157  enum Source
158  {
159    /**
160     * The input value is passed to the view during construction.
161     */
162    DEFAULT,
163    /**
164     * The input value is retrieved from the react context.
165     */
166    CONTEXT
167  }
168}