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