001package arez.annotations;
002
003import arez.ComputableValue;
004import arez.EqualityComparator;
005import java.lang.annotation.Documented;
006import java.lang.annotation.ElementType;
007import java.lang.annotation.Target;
008import javax.annotation.Nonnull;
009
010/**
011 * Methods marked with this annotation are <a href="https://en.wikipedia.org/wiki/Memoization">memoized</a> while
012 * activated which typically means they have an observer. These methods are typically backed with one or more
013 * {@link ComputableValue} instances.
014 *
015 * <p>The return value should be derived from the method parameters and any other {@link Observable} properties
016 * or {@link Memoize} properties accessed within the scope of the method. The he value returned by the method
017 * should not change unless the state of the {@link Observable} properties or {@link Memoize} properties change.
018 * If the return value can change outside of the above scenarios it is important to set the {@link #depType()}
019 * to {@link DepType#AREZ_OR_EXTERNAL} and explicitly report possible changes to the derived value by invoking
020 * the {@link ComputableValue#reportPossiblyChanged()} on the {@link ComputableValue} returned from a method
021 * annotated by the {@link ComputableValueRef} that is linked to the method marked with this annotation.</p>
022 *
023 * <p>The method is wrapped in a READ_ONLY transaction and thus can not modify other state in the system.</p>
024 *
025 * <p>The enhanced method is implemented by creating a separate {@link ComputableValue} instance for each unique
026 * combination of parameters that are passed to the method. When the {@link ComputableValue} is deactivated, a hook
027 * triggers that removes the {@link ComputableValue} from the local cache. If the method has zero parameter then
028 * the method is backed by a single {@link ComputableValue} instance.</p>
029 *
030 * <p>The method that is annotated with this annotation must comply with the additional constraints:</p>
031 * <ul>
032 * <li>Must not be annotated with any other arez annotation</li>
033 * <li>Must return a value</li>
034 * <li>Must not be private</li>
035 * <li>Must not be static</li>
036 * <li>Must not be final</li>
037 * <li>Must not be abstract</li>
038 * <li>Must not throw exceptions</li>
039 * <li>Must be accessible to the class annotated by the {@link ArezComponent} annotation.</li>
040 * </ul>
041 *
042 * <p>This annotation is only supported on elements contained within a type annotated by
043 * {@link ArezComponent} or {@link ArezComponentLike}. Other usages will fail compilation.</p>
044 */
045@Documented
046@Target( ElementType.METHOD )
047public @interface Memoize
048{
049  /**
050   * Return the root name of the element value relative to the component.
051   * If the method has parameters then the name will be used in combination with a sequence
052   * when naming the synthesized {@link ComputableValue} instances. The value must conform to
053   * the requirements of a java identifier. The name must also be unique across {@link Observable}s,
054   * {@code Memoize}s and {@link Action}s within the scope of the {@link ArezComponent} annotated element.
055   *
056   * @return the root name of the element relative to the component.
057   */
058  @Nonnull
059  String name() default "<default>";
060
061  /**
062   * A flag indicating whether the computable should be "kept alive". A computable that is kept alive
063   * is activated on creation and never deactivates. This is useful if the computable property is only
064   * accessed from within actions but should be kept up to date and not recomputed on each access.
065   * This MUST not be set if the target method has any parameters as can not keep computed value active
066   * if parameter values are unknown.
067   *
068   * @return true to keep computable alive.
069   */
070  boolean keepAlive() default false;
071
072  /**
073   * The priority of the underlying ComputableValue observer.
074   *
075   * @return the priority of the ComputableValue observer.
076   */
077  Priority priority() default Priority.DEFAULT;
078
079  /**
080   * Flag controlling whether the underlying observer can observe ComputableValue instances with lower priorities.
081   * The default value of false will result in an invariant failure (in development mode) if a lower priority
082   * dependency is observed by the observer. This is to prevent priority inversion when scheduling a higher
083   * priority observer is dependent upon a lower priority computable value. If the value is true then the no
084   * invariant failure is triggered and the component relies on the component author to handle possible priority
085   * inversion.
086   *
087   * @return false if observing lower priority dependencies should result in invariant failure in development mode.
088   */
089  boolean observeLowerPriorityDependencies() default false;
090
091  /**
092   * Enum indicating whether the value of the computable is derived from arez elements and/or external dependencies.
093   * If set to {@link DepType#AREZ} then Arez will verify that the method annotated by this annotation accesses arez
094   * elements (i.e. instances of {@link arez.ObservableValue} or instances of {@link ComputableValue}). If set to
095   * {@link DepType#AREZ_OR_NONE} then the runtime will allow computable to exist with no dependencies. If set
096   * to {@link DepType#AREZ_OR_EXTERNAL} then the component must define a {@link ComputableValueRef} method and should invoke
097   * {@link ComputableValue#reportPossiblyChanged()} when the non-arez dependencies are changed.
098   *
099   * @return the types of dependencies allowed on the computable.
100   */
101  @Nonnull
102  DepType depType() default DepType.AREZ;
103
104  /**
105   * Return true if the return value of the memoized value should be reported to the Arez spy subsystem.
106   * It is useful to disable reporting for large, circular or just uninteresting parameters to the spy infrastructure.
107   *
108   * @return true to report the return value, false otherwise.
109   */
110  boolean reportResult() default true;
111
112  /**
113   * Indicate whether the memoized value can be read outside a transaction.
114   * If the value is {@link Feature#AUTODETECT} then the value will be derived from the
115   * {@link ArezComponent#defaultReadOutsideTransaction()} parameter on the {@link ArezComponent} annotation. If
116   * the value is set to {@link Feature#ENABLE} then the memoized value can be read outside a transaction. It should
117   * be noted that in this scenario the memoized value will be recalculated each time it is accessed.
118   *
119   * @return flag that determines whether the memoized value allows reads outside a transaction, false to require a transaction to read the memoized value.
120   */
121  Feature readOutsideTransaction() default Feature.AUTODETECT;
122
123  /**
124   * Return the strategy used to compare computed values when deciding whether to
125   * propagate a change. If this parameter is left as {@link EqualityComparator} then the
126   * annotation processor will derive the comparator from the exact declared return type when
127   * that type is annotated with {@link DefaultEqualityComparator}. If no type-level default is
128   * present then {@link arez.ObjectsEqualsComparator} will be used.
129   *
130   * <p>Explicitly specifying a comparator on the annotation overrides any type-level default.
131   * Type-level defaults are not derived for arrays, type-use annotations, or supertypes and
132   * interfaces other than the exact declared type.</p>
133   *
134   * @return the comparator type used when checking whether values are equal.
135   */
136  @Nonnull
137  Class<? extends EqualityComparator> equalityComparator() default EqualityComparator.class;
138}