Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start cleanup of AnnotationTarget #507

Merged
merged 3 commits into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package jakarta.enterprise.inject.build.compatible.spi;

import jakarta.enterprise.lang.model.AnnotationAttribute;
import jakarta.enterprise.lang.model.AnnotationMember;
import jakarta.enterprise.lang.model.AnnotationInfo;
import jakarta.enterprise.lang.model.declarations.ClassInfo;
import java.lang.annotation.Annotation;
Expand All @@ -9,9 +9,9 @@
// TODO better name?
// TODO devise a builder-style API instead (see also Annotations)
public interface AnnotationConfig {
void addAnnotation(Class<? extends Annotation> annotationType, AnnotationAttribute... attributes);
void addAnnotation(Class<? extends Annotation> annotationType, AnnotationMember... attributes);

void addAnnotation(ClassInfo<?> annotationType, AnnotationAttribute... attributes);
void addAnnotation(ClassInfo<?> annotationType, AnnotationMember... attributes);

void addAnnotation(AnnotationInfo annotation);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,84 +1,84 @@
package jakarta.enterprise.inject.build.compatible.spi;

import jakarta.enterprise.lang.model.AnnotationAttribute;
import jakarta.enterprise.lang.model.AnnotationAttributeValue;
import jakarta.enterprise.lang.model.AnnotationMember;
import jakarta.enterprise.lang.model.AnnotationMemberValue;
import jakarta.enterprise.lang.model.AnnotationInfo;
import jakarta.enterprise.lang.model.declarations.ClassInfo;
import java.lang.annotation.Annotation;
import java.util.List;

// TODO devise a builder-style API instead (see also AnnotationConfig)
public interface Annotations {
AnnotationAttributeValue value(boolean value);
AnnotationMemberValue value(boolean value);

AnnotationAttributeValue value(byte value);
AnnotationMemberValue value(byte value);

AnnotationAttributeValue value(short value);
AnnotationMemberValue value(short value);

AnnotationAttributeValue value(int value);
AnnotationMemberValue value(int value);

AnnotationAttributeValue value(long value);
AnnotationMemberValue value(long value);

AnnotationAttributeValue value(float value);
AnnotationMemberValue value(float value);

AnnotationAttributeValue value(double value);
AnnotationMemberValue value(double value);

AnnotationAttributeValue value(char value);
AnnotationMemberValue value(char value);

AnnotationAttributeValue value(String value);
AnnotationMemberValue value(String value);

AnnotationAttributeValue value(Enum<?> enumValue);
AnnotationMemberValue value(Enum<?> enumValue);

AnnotationAttributeValue value(Class<? extends Enum<?>> enumType, String enumValue);
AnnotationMemberValue value(Class<? extends Enum<?>> enumType, String enumValue);

AnnotationAttributeValue value(ClassInfo<?> enumType, String enumValue);
AnnotationMemberValue value(ClassInfo<?> enumType, String enumValue);

AnnotationAttributeValue value(Class<?> value);
AnnotationMemberValue value(Class<?> value);

AnnotationAttributeValue annotationValue(Class<? extends Annotation> annotationType, AnnotationAttribute... attributes);
AnnotationMemberValue annotationValue(Class<? extends Annotation> annotationType, AnnotationMember... attributes);

AnnotationAttributeValue annotationValue(ClassInfo<?> annotationType, AnnotationAttribute... attributes);
AnnotationMemberValue annotationValue(ClassInfo<?> annotationType, AnnotationMember... attributes);

AnnotationAttributeValue annotationValue(AnnotationInfo annotation);
AnnotationMemberValue annotationValue(AnnotationInfo annotation);

AnnotationAttributeValue annotationValue(Annotation annotation);
AnnotationMemberValue annotationValue(Annotation annotation);

AnnotationAttribute attribute(String name, boolean value);
AnnotationMember attribute(String name, boolean value);

AnnotationAttribute attribute(String name, byte value);
AnnotationMember attribute(String name, byte value);

AnnotationAttribute attribute(String name, short value);
AnnotationMember attribute(String name, short value);

AnnotationAttribute attribute(String name, int value);
AnnotationMember attribute(String name, int value);

AnnotationAttribute attribute(String name, long value);
AnnotationMember attribute(String name, long value);

AnnotationAttribute attribute(String name, float value);
AnnotationMember attribute(String name, float value);

AnnotationAttribute attribute(String name, double value);
AnnotationMember attribute(String name, double value);

AnnotationAttribute attribute(String name, char value);
AnnotationMember attribute(String name, char value);

AnnotationAttribute attribute(String name, String value);
AnnotationMember attribute(String name, String value);

AnnotationAttribute attribute(String name, Enum<?> enumValue);
AnnotationMember attribute(String name, Enum<?> enumValue);

AnnotationAttribute attribute(String name, Class<? extends Enum<?>> enumType, String enumValue);
AnnotationMember attribute(String name, Class<? extends Enum<?>> enumType, String enumValue);

AnnotationAttribute attribute(String name, ClassInfo<?> enumType, String enumValue);
AnnotationMember attribute(String name, ClassInfo<?> enumType, String enumValue);

AnnotationAttribute attribute(String name, Class<?> value);
AnnotationMember attribute(String name, Class<?> value);

AnnotationAttribute arrayAttribute(String name, AnnotationAttributeValue... values);
AnnotationMember arrayAttribute(String name, AnnotationMemberValue... values);

AnnotationAttribute arrayAttribute(String name, List<AnnotationAttributeValue> values);
AnnotationMember arrayAttribute(String name, List<AnnotationMemberValue> values);

AnnotationAttribute annotationAttribute(String name, Class<? extends Annotation> annotationType,
AnnotationAttribute... attributes);
AnnotationMember annotationAttribute(String name, Class<? extends Annotation> annotationType,
AnnotationMember... attributes);

AnnotationAttribute annotationAttribute(String name, ClassInfo<?> annotationType, AnnotationAttribute... attributes);
AnnotationMember annotationAttribute(String name, ClassInfo<?> annotationType, AnnotationMember... attributes);

AnnotationAttribute annotationAttribute(String name, AnnotationInfo annotation);
AnnotationMember annotationAttribute(String name, AnnotationInfo annotation);

AnnotationAttribute annotationAttribute(String name, Annotation annotation);
AnnotationMember annotationAttribute(String name, Annotation annotation);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
* a parameter of type {@link Types Types}. It provides factory methods for the void type, primitive types,
* class types, array types, parameterized types and wildcard types.
* <p>
* If you need to create instances of {@link jakarta.enterprise.lang.model.AnnotationAttribute AnnotationAttribute} or
* {@link jakarta.enterprise.lang.model.AnnotationAttributeValue AnnotationAttributeValue}, you can also declare
* If you need to create instances of {@link jakarta.enterprise.lang.model.AnnotationMember AnnotationAttribute} or
* {@link jakarta.enterprise.lang.model.AnnotationMemberValue AnnotationAttributeValue}, you can also declare
* a parameter of type {@link Annotations Annotations}. It provides factory methods for all kinds of annotation attributes.
*/
@Target(ElementType.METHOD)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package jakarta.enterprise.inject.build.compatible.spi;

import jakarta.enterprise.lang.model.AnnotationAttribute;
import jakarta.enterprise.lang.model.AnnotationMember;
import jakarta.enterprise.lang.model.AnnotationInfo;
import jakarta.enterprise.lang.model.declarations.ClassInfo;
import jakarta.enterprise.lang.model.types.Type;
Expand All @@ -25,9 +25,9 @@ public interface SyntheticBeanBuilder<T> {

// can be called multiple times and is additive
// TODO methods to add multiple qualifiers at once?
SyntheticBeanBuilder<T> qualifier(Class<? extends Annotation> qualifierAnnotation, AnnotationAttribute... attributes);
SyntheticBeanBuilder<T> qualifier(Class<? extends Annotation> qualifierAnnotation, AnnotationMember... attributes);

SyntheticBeanBuilder<T> qualifier(ClassInfo<?> qualifierAnnotation, AnnotationAttribute... attributes);
SyntheticBeanBuilder<T> qualifier(ClassInfo<?> qualifierAnnotation, AnnotationMember... attributes);

SyntheticBeanBuilder<T> qualifier(AnnotationInfo qualifierAnnotation);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package jakarta.enterprise.inject.build.compatible.spi;

import jakarta.enterprise.lang.model.AnnotationAttribute;
import jakarta.enterprise.lang.model.AnnotationMember;
import jakarta.enterprise.lang.model.AnnotationInfo;
import jakarta.enterprise.lang.model.declarations.ClassInfo;
import jakarta.enterprise.lang.model.types.Type;
Expand Down Expand Up @@ -40,9 +40,9 @@ public interface SyntheticObserverBuilder {

// can be called multiple times and is additive
// TODO methods to add multiple qualifiers at once?
SyntheticObserverBuilder qualifier(Class<? extends Annotation> qualifierAnnotation, AnnotationAttribute... attributes);
SyntheticObserverBuilder qualifier(Class<? extends Annotation> qualifierAnnotation, AnnotationMember... attributes);

SyntheticObserverBuilder qualifier(ClassInfo<?> qualifierAnnotation, AnnotationAttribute... attributes);
SyntheticObserverBuilder qualifier(ClassInfo<?> qualifierAnnotation, AnnotationMember... attributes);

SyntheticObserverBuilder qualifier(AnnotationInfo qualifierAnnotation);

Expand Down
61 changes: 43 additions & 18 deletions api/src/main/java/jakarta/enterprise/lang/model/AnnotationInfo.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
package jakarta.enterprise.lang.model;

import jakarta.enterprise.lang.model.declarations.ClassInfo;

import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.util.Collection;

public interface AnnotationInfo {
/**
* Models an annotation definition, providing access to the {@link AnnotationMemberValue}
* instances.
*
* @param <T> The annotation type.
*/
public interface AnnotationInfo<T extends Annotation> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this needs to be parameterized, but let's discuss that later.

(As for ClassInfo, I'd pretty much love to remove the type parameter -- I originally added it to be able to express a query, but we don't allow "injecting" ClassInfo anymore and we want ClassConfig to not inherit from ClassInfo, so that reason no longer applies.)

/**
* The commonly used {@code value()} member.
*/
String MEMBER_VALUE = "value";

/**
* Target of this annotation.
* That is, the declaration, the type parameter or the type use on which this annotation is present.
* TODO what if this annotation is a nested annotation?
* TODO what if this annotation doesn't have a known target (e.g. qualifier of a synthetic bean)?
* TODO Do we need this? Retrieving the target from an annotation value is not supported in Micronaut
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can probably remove this.

In Quarkus, the equivalent is used quite often, because we often start by simply looking up all annotations of some type, and then filter based on their target etc. But at the moment, I can't think of a way that would enable something like this in CDI Lite. I think we only allow top-down access, never bottom-up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I will remove it in the PR

*
* @return target of this annotation
*/
Expand All @@ -18,15 +32,15 @@ public interface AnnotationInfo {
/**
* Declaration of this annotation's type.
*
* @return declaration of this annotation
* @return declaration of this annotation, never {@code null}
*/
ClassInfo<?> declaration();
ClassInfo<T> declaration();

/**
* Fully qualified name of this annotation.
* Equivalent to {@code declaration().name()}.
*
* @return fully qualified name of this annotation
* @return fully qualified name of this annotation, never {@code null}
Copy link
Contributor

@Ladicek Ladicek Jul 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Off topic, since this was introduced earlier and you didn't change it, but I want to bring this up anyway.

I recently learned that the JLS makes an important distinction between fully qualified name and binary name. They are identical for top-level types, but not for nested types.

For example:

package com.example;

public class MyClass {
    public @interface MyAnnotation {
    }
}

Fully qualified name of the MyAnnotation type is com.example.MyClass.MyAnnotation, while binary name is com.example.MyClass$MyAnnotation.

I think we should probably settle on binary names, and I started doing that here: https://github.com/eclipse-ee4j/cdi/pull/506/files#diff-a8c5a19048a48d991137c3ecb9f78f593b33a264b81a41bd5fa543cf9ac7ae73

WDYT about that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for binary names

*/
default String name() {
return declaration().name();
Expand All @@ -43,34 +57,45 @@ default boolean isRepeatable() {
}

/**
* Whether this annotation has an attribute with given {@code name}.
* Whether this annotation has a member with given {@code name}.
*
* @param name attribute name
* @return whether this annotation has an attribute with given {@code name}
* @param name member name, never {@code null}
* @return whether this annotation has a member with given {@code name}
* @throws java.lang.IllegalArgumentException if the argument is {@code null}
*/
boolean hasAttribute(String name);
boolean hasMember(String name);

/**
* Value of this annotation's attribute with given {@code name}.
* TODO what if it doesn't exist? null, exception, or change return type to Optional
*
* @param name attribute name
* @return value of this annotation's attribute with given {@code name}
* @param name attribute name, never {@code null}
* @return value of this annotation's attribute with given {@code name} or {@code null} if it doesn't exist.
* @throws java.lang.IllegalArgumentException if the argument is {@code null}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about the distintion between NullPointerException and IllegalArgumentException for null arguments? I personally like IllegalArgumentException more, but the convention seems to be (and Effective Java advises the same) to use NPE.

On the other hand, if the rest of CDI uses IAE, then I'd say we follow suit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps NullPointerException is fine, consistency is the key. I have tended to use IAE but then again methods like Objects.requireNonNull throw NPE

*/
AnnotationAttributeValue attribute(String name);
AnnotationMemberValue member(String name);

/**
* Returns whether this annotation has a value defined using the {@link #MEMBER_VALUE} member.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Returns whether this annotation has a value defined using the {@link #MEMBER_VALUE} member.
* Returns whether this annotation has the {@link #MEMBER_VALUE} member.

Reads better to me?

*
* @return Returns {@code true} if the {@link #MEMBER_VALUE} is set, {@code false} otherwise
*/
default boolean hasValue() {
return hasAttribute("value");
return hasMember(MEMBER_VALUE);
}

default AnnotationAttributeValue value() {
return attribute("value");
/**
* Returns the {@link AnnotationMemberValue} instance that represents
* the value of the {@link #MEMBER_VALUE} member.
* @return An {@link AnnotationMemberValue} instance or {@code null} if none exists.
*/
default AnnotationMemberValue value() {
return member(MEMBER_VALUE);
}

/**
* All attributes of this annotation.
* All members of this annotation.
*
* @return all attributes of this annotation
* @return An immutable collection of all members of this annotation. Never {@code null}.
*/
Collection<AnnotationAttribute> attributes();
Collection<AnnotationMember> members();
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package jakarta.enterprise.lang.model;

// TODO "attribute" is a colloquial expression, perhaps use something closer to the JLS? AnnotationMember?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to AnnotationMember, we can remove this TODO comment.

public interface AnnotationAttribute {
public interface AnnotationMember {
String name();

AnnotationAttributeValue value();
AnnotationMemberValue value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.util.List;

// TODO "attribute" is a colloquial expression, perhaps use something closer to the JLS? AnnotationMember?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to AnnotationMemberValue, we can remove this TODO comment.

public interface AnnotationAttributeValue {
public interface AnnotationMemberValue {
// TODO is there a better API for this than the is*/as* style?

enum Kind {
Expand Down Expand Up @@ -105,7 +105,7 @@ default boolean isNestedAnnotation() {

Type asClass(); // can be a VoidType, PrimitiveType or ClassType

List<AnnotationAttributeValue> asArray();
List<AnnotationMemberValue> asArray();

AnnotationInfo asNestedAnnotation();
AnnotationInfo<?> asNestedAnnotation();
}
Loading