001package arez.annotations;
002
003import arez.EqualityComparator;
004import arez.ObservableValue;
005import java.lang.annotation.Documented;
006import java.lang.annotation.ElementType;
007import java.lang.annotation.Target;
008import javax.annotation.Nonnull;
009
010/**
011 * Annotation applied to methods that expose an ObservableValue value in Arez.
012 * Methods annotated with this either query state or mutate state. The query
013 * method is expected to have 0 parameters and return a value and by default
014 * is named with "get" or "is" prefixed to the property name. The mutation
015 * method is expected to have a single parameter and return no value and by
016 * default is named with "set" prefixed to property name. The setter or getter
017 * can also be named matching the property name without the prefix.
018 *
019 * <p>Only one of the query or mutation method needs to be annotated with
020 * this annotation if the other method follows the normal conventions. If
021 * the other method does not conform to conventions, then you will need to
022 * annotate the pair and specify a value for {@link #name()}.</p>
023 *
024 * <p>The method should only invoked within the scope of a transaction.
025 * The mutation method requires that the transaction be READ_WRITE.</p>
026 *
027 * <p>The method that is annotated with this annotation must also comply with the following constraints:</p>
028 * <ul>
029 * <li>Must not be annotated with any other arez annotation</li>
030 * <li>Must not be private</li>
031 * <li>Must not be static</li>
032 * <li>Must not be final</li>
033 * <li>May be abstract but if abstract then the paired setter or getter must also be abstract</li>
034 * <li>Must be accessible to the class annotated by the {@link ArezComponent} annotation.</li>
035 * </ul>
036 *
037 * <p>This annotation is only supported on methods contained within a type annotated by
038 * {@link ArezComponent} or {@link ArezComponentLike}. Other usages will fail compilation.</p>
039 */
040@Documented
041@Target( ElementType.METHOD )
042public @interface Observable
043{
044  /**
045   * Return the name of the ObservableValue relative to the component. If not specified
046   * will default to the name of the property by convention as described above.
047   * The value must conform to the requirements of a java identifier.
048   * The name must also be unique across {@link Observable}s,
049   * {@link Memoize}s and {@link Action}s within the scope of the
050   * {@link ArezComponent} annotated element.
051   *
052   * @return the name of the ObservableValue relative to the component.
053   */
054  @Nonnull
055  String name() default "<default>";
056
057  /**
058   * Set this to false if there is no setter method and the component is
059   * expected to use {@link ObservableValueRef} to indicate when value has changed.
060   *
061   * @return true if there is expected to be a setter, false if there should be no setter.
062   */
063  boolean expectSetter() default true;
064
065  /**
066   * Indicate whether the generated component class should add a parameter to the constructor to initialize this property.
067   * This parameter should only be set to {@link Feature#ENABLE} when the observable property is defined by a
068   * pair of abstract methods. If set to {@link Feature#AUTODETECT} then an initializer will be added for an
069   * observable property if it is defined by a pair of abstract methods and the values is annotated with the
070   * {@link Nonnull} annotation and it is not annotated by {@link Inverse}.
071   * It is an error to set this parameter to {@link Feature#ENABLE} when the property has an
072   * {@link ObservableInitial} annotation. If {@link ObservableInitial} is present and this parameter is
073   * {@link Feature#AUTODETECT}, the initializer is treated as {@link Feature#DISABLE}.
074   *
075   * <p>The initializer parameters will be added as additional parameters at the end of the parameter list in
076   * the generated classes constructors. The initializers will be defined in the order that the observable
077   * properties are declared. They properties be assigned after the parent constructor has been invoked.</p>
078   *
079   * @return flag controlling whether a parameter should be added to the constructor to initialize the property.
080   */
081  @Nonnull
082  Feature initializer() default Feature.AUTODETECT;
083
084  /**
085   * Indicate whether the observable can be read outside a transaction.
086   * If the value is {@link Feature#AUTODETECT} then the value will be derived from the
087   * {@link ArezComponent#defaultReadOutsideTransaction()} parameter on the {@link ArezComponent} annotation. If
088   * the value is set to {@link Feature#ENABLE} then the observable can be read outside a transaction and the
089   * {@link ObservableValue#reportObserved()} will only be invoked if the observables is accessed from within
090   * a tracking transaction (i.e. when an {@link arez.Observer} or {@link arez.ComputableValue} creates the
091   * transaction). Thus, {@link Action} annotated methods that only access observables that set the
092   * readOutsideTransaction parameter to {@link Feature#ENABLE} and neither access nor modify other arez elements
093   * no longer need to be annotated with {@link Action} annotations.
094   *
095   * @return flag that determines whether the observable allows reads outside a transaction, false to require a transaction to read the observable.
096   */
097  Feature readOutsideTransaction() default Feature.AUTODETECT;
098
099  /**
100   * Return true if the observable will create an action if the write occurs outside a transaction.
101   *
102   * @return true to allow writes to create an action if needed, false to require a transaction to write observable.
103   */
104  Feature writeOutsideTransaction() default Feature.AUTODETECT;
105
106  /**
107   * Return false if the setter should verify observable value has changed before propagating change.
108   * In some scenarios, the setter method will modify the value before updating the observable or may
109   * decide to abort the update. This setting will force the generated code to check the value of the
110   * observable property after the setter and only invoke {@link ObservableValue#reportChanged()} if
111   * a change has actually occurred.
112   *
113   * <p>This parameter should not be set to false if the associated setter is abstract. It is also
114   * invalid to set this value to false if {@link #expectSetter()} is false.</p>
115   *
116   * @return false if the setter should verify observable value has changed before propagating change.
117   */
118  boolean setterAlwaysMutates() default true;
119
120  /**
121   * Return the strategy used to compare values when checking whether the observable has changed.
122   * If this parameter is left as {@link EqualityComparator} then the annotation processor will
123   * derive the comparator from the exact declared observable type when that type is annotated
124   * with {@link DefaultEqualityComparator}. If no type-level default is present then
125   * {@link arez.ObjectsEqualsComparator} will be used.
126   *
127   * <p>Explicitly specifying a comparator on the annotation overrides any type-level default.
128   * Type-level defaults are not derived for arrays, type-use annotations, or supertypes and
129   * interfaces other than the exact declared type.</p>
130   *
131   * @return the comparator type used when checking whether values are equal.
132   */
133  @Nonnull
134  Class<? extends EqualityComparator> equalityComparator() default EqualityComparator.class;
135}