001package arez.annotations;
002
003import java.lang.annotation.Documented;
004import java.lang.annotation.ElementType;
005import java.lang.annotation.Target;
006import javax.annotation.Nonnull;
007import org.intellij.lang.annotations.Language;
008
009/**
010 * Annotation applied to methods that define a single "contextual" parameter to one or more {@link Memoize}
011 * annotated methods. A "contextual" parameter is one that is used within the {@link Memoize} annotated methods,
012 * but is not passed in but derived from the calling context. These are the conceptual equivalent of thread-local
013 * values accessed from within the function.
014 *
015 * <p>There are expected to be a three methods for each context parameter: one to get the value from the
016 * calling context (optionally prefixed with "capture" that returns the type of the context parameter), one
017 * to push the value to the calling context (prefixed with "push" with a single parameter that has the
018 * type of the context parameter), and one to pop the value from the calling context (prefixed with "pop"
019 * with a single parameter that has the type of the context parameter).</p>
020 *
021 * <h2>Capture Method</h2>
022 *
023 * <p>The method to capture the context parameter is invoked prior to the invocation of the {@link Memoize}
024 * annotated method when non-arez-framework code invokes the method. It may be invoked outside of an arez
025 * transaction if the associated method has {@link Memoize#readOutsideTransaction()} resolve to
026 * {@link Feature#DISABLE}. The method must also comply with the following additional constraints:</p>
027 *
028 * <ul>
029 * <li>Must not be annotated with any other arez annotation</li>
030 * <li>Must return a value</li>
031 * <li>Must not have any parameters</li>
032 * <li>Must not specify type parameters</li>
033 * <li>Must not be private</li>
034 * <li>Must not be static</li>
035 * <li>Must not be abstract</li>
036 * <li>Must not throw exceptions</li>
037 * <li>Must be accessible to the class annotated by the {@link ArezComponent} annotation.</li>
038 * </ul>
039 *
040 * <h2>Push Method</h2>
041 *
042 * <p>The method to push the context parameter into the current context is invoked prior to the invocation
043 * of the {@link Memoize} annotated method when the arez framework invokes the method to determine whether
044 * the result has changed. It is invoked outside of an arez transaction. The method must also comply
045 * with the following additional constraints:</p>
046 *
047 * <p>The method that is annotated with this annotation must comply with the additional constraints:</p>
048 * <ul>
049 * <li>The method name must start with "push"</li>
050 * <li>Must not be annotated with any other arez annotation</li>
051 * <li>Must not return a value</li>
052 * <li>Must have one parameter</li>
053 * <li>Must not specify type parameters</li>
054 * <li>Must not be private</li>
055 * <li>Must not be static</li>
056 * <li>Must not be abstract</li>
057 * <li>Must not throw exceptions</li>
058 * <li>Must be accessible to the class annotated by the {@link ArezComponent} annotation.</li>
059 * </ul>
060 *
061 * <h2>Pop Method</h2>
062 *
063 * <p>The method to pop the context parameter from the current context is invoked after to the invocation
064 * of the {@link Memoize} annotated method when the arez framework invokes the method to determine whether
065 * the result has changed. It is invoked outside of an arez transaction. The method must also comply
066 * with the following additional constraints:</p>
067 *
068 * <p>The method that is annotated with this annotation must comply with the additional constraints:</p>
069 * <ul>
070 * <li>The method name must start with "pop"</li>
071 * <li>Must not be annotated with any other arez annotation</li>
072 * <li>Must not return a value</li>
073 * <li>Must have one parameter</li>
074 * <li>Must not specify type parameters</li>
075 * <li>Must not be private</li>
076 * <li>Must not be static</li>
077 * <li>Must not be abstract</li>
078 * <li>Must not throw exceptions</li>
079 * <li>Must be accessible to the class annotated by the {@link ArezComponent} annotation.</li>
080 * </ul>
081 */
082@Documented
083@Target( ElementType.METHOD )
084public @interface MemoizeContextParameter
085{
086  /**
087   * Return the name of the context parameter.
088   * If not specified, then the name will be derived from the name of the method.
089   * <ul>
090   *   <li>To derive the name from a push method then remove the "push" prefix.</li>
091   *   <li>To derive the name from a pop method then remove the "pop" prefix.</li>
092   *   <li>To derive the name from a capture method then remove the optional "capture" prefix else just use the method name if no such prefix.</li>
093   * </ul>
094   *
095   * @return the name of the context parameter.
096   */
097  @Nonnull
098  String name() default "<default>";
099
100  /**
101   * Return a regular expression for matching the arez names of memoized methods where this context parameter is tracked.
102   *
103   * @return a regular expression for matching arez name of memoized methods.
104   */
105  @Nonnull
106  @Language( "RegExp" )
107  String pattern() default ".*";
108
109  /**
110   * Return true if the component does not need to have {@link Memoize} annotated methods match.
111   * Otherwise, if no {@link Memoize} annotated methods match the {@link #pattern()} then the annotation
112   * processor will generate an error.
113   *
114   * @return true if the memoized methods must match annotation, false otherwise.
115   */
116  boolean allowEmpty() default false;
117}