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}