Skip to content

Commit

Permalink
improve extension API documentation and resolve some open questions
Browse files Browse the repository at this point in the history
- modified the `@Processing` phase to also execute for synthetic beans
  and observers, after `@Synthesis` is finished
- added annotation transformations for method parameters (`ParameterConfig`)
- changed `ArrayType` to use a component type representation,
  instead of a representation with element type + number of dimensions
- clarified what happens with non-existing annotation members defined
  using `AnnotationBuilder` in the resulting `AnnotationInfo`
- clarified the set of error conditions for `Types.ofArray()`
- settled on the current design for `Parameters`, removed the comment
  that outlines another possible design
- removed some TODOs where alternative design options exist, but they
  have no obvious advantage (one design simply has to be chosen)
  • Loading branch information
Ladicek committed Oct 5, 2021
1 parent 602f89c commit a626d76
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
* Note that values of all members of given annotation type must be defined before
* calling {@code build()}, except of annotation members that declare a default value.
* If a value is not defined for an annotation member that does not have a default value,
* {@code build()} will throw an exception. Defining values of members that do not
* exist on given annotation type is possible, but such values will be ignored.
* {@code build()} will throw an exception.
* <p>
* Defining values of members that do not exist on given annotation type is possible,
* and such members will be retained in the resulting {@code AnnotationInfo}. However,
* if that {@code AnnotationInfo} is later transformed to an instance of the annotation
* type, the non-existing members will disappear.
*
* @since 4.0
*/
// TODO not sure if all the methods taking ClassInfo are needed, maybe they're not
public interface AnnotationBuilder {
/**
* Returns a new {@link AnnotationBuilder} that builds an annotation of given type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* <li>{@link ClassConfig} or {@link jakarta.enterprise.lang.model.declarations.ClassInfo ClassInfo}</li>
* <li>{@link MethodConfig} or {@link jakarta.enterprise.lang.model.declarations.MethodInfo MethodInfo}</li>
* <li>{@link FieldConfig} or {@link jakarta.enterprise.lang.model.declarations.FieldInfo FieldInfo}</li>
* <li>({@code ParameterConfig} or {@code ParameterInfo} is not possible, parameters must be accessed
* through {@code MethodConfig} or {@code MethodInfo})</li>
* </ul>
* The method must also have at least one annotation {@link ExactType @ExactType} or {@link SubtypesOf @SubtypesOf}.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import jakarta.enterprise.lang.model.declarations.MethodInfo;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.function.Predicate;

/**
Expand Down Expand Up @@ -69,4 +70,11 @@ public interface MethodConfig extends DeclarationConfig {
*/
@Override
MethodConfig removeAllAnnotations();

/**
* Returns a list of {@link ParameterConfig} objects for each parameter of this method.
*
* @return immutable list of {@link ParameterConfig} objects, never {@code null}
*/
List<ParameterConfig> parameters();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package jakarta.enterprise.inject.build.compatible.spi;

import jakarta.enterprise.lang.model.AnnotationInfo;
import jakarta.enterprise.lang.model.declarations.ParameterInfo;

import java.lang.annotation.Annotation;
import java.util.function.Predicate;

/**
* Allows adding annotations to and removing annotations from a method parameter.
* Note that the method parameter is not physically altered, the modifications
* are only seen by the CDI container.
*
* @see Enhancement
* @since 4.0
*/
public interface ParameterConfig extends DeclarationConfig {
/**
* Returns the {@link ParameterInfo} corresponding to this transformed method parameter.
*
* @return the {@link ParameterInfo} corresponding to this transformed method parameter, never {@code null}
*/
@Override
ParameterInfo info();

/**
* Adds a marker annotation of given type to this method parameter.
* Does not allow configuring annotation members.
*
* @param annotationType the annotation type, must not be {@code null}
* @return this configurator object, to allow fluent usage
*/
@Override
ParameterConfig addAnnotation(Class<? extends Annotation> annotationType);

/**
* Adds given annotation to this method parameter. The {@link AnnotationInfo} can be obtained
* from an annotation target, or constructed from scratch using {@link AnnotationBuilder}.
*
* @param annotation the annotation to add to this method parameter, must not be {@code null}
* @return this configurator object, to allow fluent usage
*/
@Override
ParameterConfig addAnnotation(AnnotationInfo annotation);

/**
* Adds given annotation to this method parameter. The annotation instance is typically
* a subclass of {@link jakarta.enterprise.util.AnnotationLiteral AnnotationLiteral}.
*
* @param annotation the annotation to add to this method parameter, must not be {@code null}
* @return this configurator object, to allow fluent usage
*/
@Override
ParameterConfig addAnnotation(Annotation annotation);

/**
* Removes all annotations matching given predicate from this method parameter.
*
* @param predicate an annotation predicate, must not be {@code null}
* @return this configurator object, to allow fluent usage
*/
@Override
ParameterConfig removeAnnotation(Predicate<AnnotationInfo> predicate);

/**
* Removes all annotations from this method parameter.
*
* @return this configurator object, to allow fluent usage
*/
@Override
ParameterConfig removeAllAnnotations();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@
* <p>
* Values of primitive types may be looked up either using the primitive type (such as
* {@code int.class}), or using the corresponding wrapper type ({@code Integer.class}).
* The return value is always of the wrapper type.
* The return value is always of the wrapper type, so that {@code null} can be returned
* when the parameter does not exist. Note that this does not apply to arrays
* of primitive types; an {@code int[]} cannot be looked up as {@code Integer[]}.
* This is because arrays are reference types and so {@code null} can be returned.
* <p>
* Class-typed parameters are available as instances of {@link Class}, even if an instance
* of {@code ClassInfo} was passed to the builder.
* <p>
* Similarly, annotation-typed parameters are always available as instances of the annotation
* type, even if an instance of {@code AnnotationInfo} was passed to the builder.
* Annotation-typed parameters are available as instances of the annotation type,
* even if an instance of {@code AnnotationInfo} was passed to the builder.
*/
public interface Parameters {
/**
* Returns the parameter with given {@code key}. The value is expected to be of given type.
* Returns the value of a parameter with given {@code key}. The value is expected to be of given {@code type}.
*
* @param key the parameter key; must not be {@code null}
* @param type the parameter type; must not be {@code null}
Expand All @@ -41,48 +44,15 @@ public interface Parameters {
<T> T get(String key, Class<T> type);

/**
* Returns the parameter with given {@code key}. The value is expected to be of given type.
* Returns the value of a parameter with given {@code key}. The value is expected to be of given {@code type}.
* If the parameter does not exist, returns {@code defaultValue}.
*
* @param key the parameter key; must not be {@code null}
* @param type the parameter type; must not be {@code null}
* @param defaultValue the value to return if parameter with given {@code key} does not exist
* @param <T> the parameter type
* @return the parameter value, or {@code defaultValue} if parameter with given {@code key} does not exist
* @throws ClassCastException if the parameter exists, but is of a different type
*/
<T> T get(String key, Class<T> type, T defaultValue);

// TODO another possible design would be (with or without the methods accepting default values):
// - Boolean getBoolean(String key)
// - boolean getBoolean(String key, boolean defaultValue)
// - boolean[] getBooleans(String key)
// - boolean[] getBooleans(String key, boolean[] defaultValues)
// - Integer getInt(String key)
// - int getInt(String key, int defaultValue)
// - int[] getInts(String key)
// - int[] getInts(String key, int[] defaultValues)
// - Long getLong(String key)
// - long getLong(String key, long defaultValue)
// - long[] getLongs(String key)
// - long[] getLongs(String key, long[] defaultValues)
// - Double getDouble(String key)
// - double getDouble(String key, double defaultValue)
// - double[] getDoubles(String key)
// - double[] getDoubles(String key, double[] defaultValues)
// - String getString(String key)
// - String getString(String key, String defaultValue)
// - String[] getStrings(String key)
// - String[] getStrings(String key, String[] defaultValues)
// - Enum<?> getEnum(String key)
// - Enum<?> getEnum(String key, Enum<?> defaultValue)
// - Enum<?>[] getEnums(String key)
// - Enum<?>[] getEnums(String key, Enum<?>[] defaultValues)
// - Class<?> getClass(String key) -- different name would be better, as java.lang.Object has getClass()
// - Class<?> getClass(String key, Class<?> defaultValue)
// - Class<?>[] getClasses(String key)
// - Class<?>[] getClasses(String key, Class<?>[] defaultValues)
// - Annotation getAnnotation(String key)
// - Annotation getAnnotation(String key, Annotation defaultValue)
// - Annotation[] getAnnotations(String key)
// - Annotation[] getAnnotations(String key, Annotation[] defaultValues)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
/**
* 3rd phase of CDI Lite extension execution.
* Allows processing registered beans and observers.
* Note that synthetic beans and observers, registered in {@link Synthesis @Synthesis}, will <i>not</i> be processed.
* <p>
* This phase is executed twice.
* For non-synthetic beans and observer, this phase is executed <em>before</em> {@linkplain Synthesis synthesis}.
* For synthetic beans and observers, this phase is executed <em>after</em> {@linkplain Synthesis synthesis}.
* <p>
* Methods annotated {@code @Processing} must define exactly one parameter of one of these types:
* <ul>
Expand All @@ -29,10 +32,8 @@
*
* @since 4.0
*/
// TODO allow @Processing methods to be executed even for synthetic components? that would break the nice
// sequential model, but being able to observe synthetic components is important
// TODO add a way to _modify_ beans? at least veto-ing should be possible
// (add BeanConfig extending BeanInfo? or some other way?)
// (add BeanConfig and/or BeanAttributesConfig, similarly to Portable Extensions? or some other way?)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Processing {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public interface SyntheticBeanCreator<T> {
* <p>
* The parameter map contains the same values that were passed to
* the {@link SyntheticBeanBuilder} that defined the synthetic bean.
* <p>
* May only return {@code null} if the synthetic bean is {@code @Dependent}.
*
* @param creationalContext the creational context, never {@code null}
* @param injectionPoint the injection point into which the new instance will be injected,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public interface SyntheticBeanDisposer<T> {
* The parameter map contains the same values that were passed to
* the {@link SyntheticBeanBuilder} that defined the synthetic bean.
*
* @param instance the synthetic bean instance, never {@code null} (TODO what if @Dependent?)
* @param instance the synthetic bean instance, never {@code null}
* @param creationalContext the creational context, never {@code null}
* @param params the parameter map, never {@code null}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,4 @@ public interface SyntheticComponents {
* @return a new {@link SyntheticObserverBuilder}, never {@code null}
*/
<T> SyntheticObserverBuilder<T> addObserver(Class<T> eventType);
// TODO is the class literal here bad for annotation processors perhaps? it shouldn't,
// we do the same thing in `addBean`
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@
*/
public interface Types {
/**
* Returns a type from the given class literal.
*
* Returns a type from given class literal.
* For example:
* <ul>
* <li>{@code of(void.class)}: same as {@code ofVoid()}</li>
* <li>{@code of(int.class)}: same as {@code ofPrimitive(PrimitiveKind.INT)}</li>
* <li>{@code of(String.class)}: same as {@code ofClass(... ClassInfo for String ...)}</li>
* <li>{@code of(int[].class)}: same as {@code ofArray(ofPrimitive(PrimitiveKind.INT), 1)}</li>
* <li>{@code of(void.class)}: same as {@link #ofVoid() ofVoid}{@code ()}</li>
* <li>{@code of(int.class)}: same as {@link #ofPrimitive(PrimitiveType.PrimitiveKind) ofPrimitive}{@code (PrimitiveKind.INT)}</li>
* <li>{@code of(String.class)}: same as {@link #ofClass(ClassInfo) ofClass}{@code (... ClassInfo for String ...)}</li>
* <li>{@code of(int[].class)}: same as {@link #ofArray(Type, int) ofArray}{@code (ofPrimitive(PrimitiveKind.INT), 1)}</li>
* <li>{@code of(String[][].class)}: same as {@code ofArray(ofClass(... ClassInfo for String ...), 2)}</li>
* </ul>
*
Expand Down Expand Up @@ -51,14 +50,12 @@ public interface Types {
* Returns a {@link ClassType} for the given binary name, as defined by <cite>The Java&trade; Language Specification</cite>;
* in other words, the class name as returned by {@link Class#getName()}.
* <p>
* Note that this method returns {@link ClassType}, so {@code name} can only be a name of a class.
* Note that this method returns {@link ClassType}, so {@code name} may only be a name of a class.
* For primitives, use {@link #ofPrimitive(PrimitiveType.PrimitiveKind)}. For arrays, use {@link #ofArray(Type, int)}.
*
* @param name the binary name of the class, must not be {@code null}
* @return the {@link ClassType} or {@code null} if the type does not exist on the application classpath
* @return the {@link ClassType} or {@code null} if the class is not present in any bean archive
*/
// TODO using the term "classpath" here seems inappropriate; perhaps replace with "if the type is not present
// in any bean archive" or something like that?
ClassType ofClass(String name);

/**
Expand All @@ -71,13 +68,16 @@ public interface Types {

/**
* Returns an {@link ArrayType} for the given {@linkplain Type element type} and number of dimensions.
* <p>
* Note that this method accepts the <em>element type</em> of an array, even though {@link ArrayType}
* uses a <em>component type</em> representation. For example, the component type of {@code String[][]}
* is {@code String[]}, while the element type is {@code String}.
*
* @param elementType the element {@link Type}, must not be {@code null}
* @param dimensions the number of dimensions
* @return the {@link ArrayType}, never {@code null}
* @throws IllegalArgumentException if the element type is an array type
* @throws IllegalArgumentException if the element type is an array type, a wildcard type, or the void pseudo-type
*/
// TODO more error conditions? e.g. void array does not make sense either
ArrayType ofArray(Type elementType, int dimensions);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* <ul>
* <li>plain classes</li>
* <li>interfaces</li>
* <li>enums (specialized kind of plain classes)</li>
* <li>enums (restricted kind of classes)</li>
* <li>annotations (specialized kind of interfaces)</li>
* </ul>
*
Expand Down Expand Up @@ -97,7 +97,6 @@ public interface ClassInfo extends DeclarationInfo {
*
* @return whether this class is a plain class
*/
// TODO better name? "plain class" is my invention
boolean isPlainClass();

/**
Expand Down Expand Up @@ -166,7 +165,7 @@ public interface ClassInfo extends DeclarationInfo {
Collection<MethodInfo> methods();

/**
* Returns a collection of {@link FieldInfo fields} declared in this class and all
* Returns a collection of {@linkplain FieldInfo fields} declared in this class and all
* its superclasses up to and excluding {@code java.lang.Object}. This includes
* {@code private} fields declared in superclasses.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@
* @since 4.0
*/
public interface DeclarationInfo extends AnnotationTarget {
// TODO reevaluate the is*/as*/kind() approach (everywhere!); maybe type checks and casts are better, maybe
// something completely different is even better

@Override
default boolean isDeclaration() {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
package jakarta.enterprise.lang.model.types;

/**
* An array type. That is, the {@linkplain #elementType() element type} and
* the {@linkplain #dimensions() number of dimensions}.
* An array type is created from a {@linkplain #componentType() component type}.
* For a component type {@code T}, the array type is written {@code T[]}.
* A component type may itself be an array type. For a component type {@code T[]},
* the array type is written {@code T[][]}. Such array type is also called
* multi-dimensional array type.
* <p>
* Array types also have an <em>element type</em>, which is obtained by repeatedly
* asking for the component type until a non-array type is returned. For example,
* the {@code String[][]} array type has an element type of {@code String}.
*
* @since 4.0
*/
public interface ArrayType extends Type {
// TODO this model (the element type + number of dimensions) might not be the best;
// specifically, it does not allow access to type-use annotations on component types
// (for example: @C int @A [] @B [] f;)

/**
* Returns the number of dimentions of this array type. In other words, the depth of nesting
* of this array type. For example, {@code int[]} has 1 dimension, while {@code String[][]} has 2 dimensions.
*
* @return the number of dimensions, never less than 1
*/
int dimensions();

/**
* Returns the element type of this array type, as defined by <cite>The Java&trade; Language Specification</cite>.
* That is, the element type is never an array type. Types of multidimensional (nested) arrays are represented
* as a single {@code ArrayType} with more than 1 {@linkplain #dimensions() dimension}.
* Returns the component type of this array type, as defined by <cite>The Java&trade; Language Specification</cite>.
* That is, in case of a single-dimensional array, the element type of the array, and in case of a multi-dimensional
* array, an array type with one less dimension.
* <p>
* For example, the component type of {@code int[]} is the {@code int} type. The component type
* of {@code String[][]} is the {@code String[]} array type, whose component type is the {@code String} type.
* <p>
* Each dimension of the array type may be annotated independently.
*
* @return the element type, never {@code null}
* @return the component type, never {@code null}
*/
Type elementType();
Type componentType();

// ---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
* @since 4.0
*/
public interface PrimitiveType extends Type {
// TODO Kind vs. PrimitiveKind?

enum PrimitiveKind {
BOOLEAN,
BYTE,
Expand Down
Loading

0 comments on commit a626d76

Please sign in to comment.