Skip to content

Commit

Permalink
Implement Attribute Injection : Apply review2
Browse files Browse the repository at this point in the history
  • Loading branch information
chickenchickenlove committed Apr 2, 2024
1 parent 94a93af commit 42ef9a2
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.function.Function;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
Expand All @@ -32,40 +33,24 @@
final class AnnotatedElementNameUtil {

/**
* Returns the value of the {@link Param} or {@link Attribute} annotation which is specified on the {@code element} if
* the value is not blank. If the value is blank, it returns the name of the specified
* {@code nameRetrievalTarget} object which is an instance of Both {@link Parameter} and {@link Attribute} {} or {@link Field}.
* Returns the value of {@link Header}, {@link Param}, {@link Attribute} if the value is not blank.
* If the value is blank, it returns the name of the specified {@code nameRetrievalTarget} object
* which is an instance of {@link Header}, {@link Param}, {@link Attribute} or {@link Field}.
*/
static String findName(Object nameRetrievalTarget, String value) {
static String findName(Object nameRetrievalTarget, String value, Function<String, String> delegate) {
requireNonNull(nameRetrievalTarget, "nameRetrievalTarget");
if (DefaultValues.isSpecified(value)) {
checkArgument(!value.isEmpty(), "value is empty.");
return value;
}
return getName(nameRetrievalTarget);
return delegate.apply(getName(nameRetrievalTarget));
}

/**
* Returns the value of the {@link Header} annotation which is specified on the {@code element} if
* the value is not blank. If the value is blank, it returns the name of the specified
* {@code nameRetrievalTarget} object which is an instance of {@link Parameter} or {@link Field}.
*
* <p>Note that the name of the specified {@code nameRetrievalTarget} will be converted as
* {@link CaseFormat#LOWER_HYPHEN} that the string elements are separated with one hyphen({@code -})
* character. The value of the {@link Header} annotation will not be converted because it is clearly
* specified by a user.
*/
static String findName(Header header, Object nameRetrievalTarget) {
requireNonNull(nameRetrievalTarget, "nameRetrievalTarget");

final String value = header.value();
if (DefaultValues.isSpecified(value)) {
checkArgument(!value.isEmpty(), "value is empty.");
return value;
}
return toHeaderName(getName(nameRetrievalTarget));
static String findName(Object nameRetrievalTarget, String value) {
return findName(nameRetrievalTarget, value, Function.identity());
}


/**
* Returns the name of the specified element or the default name if it can't get.
*/
Expand All @@ -89,7 +74,8 @@ static String getName(Object element) {
"cannot obtain the name of the parameter or field automatically. " +
"Please make sure you compiled your code with '-parameters' option. " +
"If not, you need to specify parameter and header names with @" +
Param.class.getSimpleName() + " and @" + Header.class.getSimpleName() + '.');
Param.class.getSimpleName() + " and @" + Header.class.getSimpleName() +
" and @" + Attribute.class.getSimpleName() + '.');
}
return parameter.getName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.findName;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.getName;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.getNameOrDefault;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.toHeaderName;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedServiceFactory.findDescription;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedServiceTypeUtil.stringToType;
import static com.linecorp.armeria.internal.server.annotation.DefaultValues.getSpecifiedValue;
Expand All @@ -45,7 +46,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -56,7 +56,6 @@
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -471,7 +470,7 @@ private static AnnotatedValueResolver of(AnnotatedElement annotatedElement,

final Header header = annotatedElement.getAnnotation(Header.class);
if (header != null) {
final String name = findName(header, typeElement);
final String name = findName(typeElement, header.value(), v -> toHeaderName(v));
return ofHeader(name, annotatedElement, typeElement, type, description);
}

Expand Down Expand Up @@ -640,37 +639,38 @@ private static AnnotatedValueResolver ofAttribute(String name,
AnnotatedElement typeElement, Class<?> type,
DescriptionInfo description) {

final List<AttributeKey<?>> attrKeys = new ArrayList<>();

final AttributeKey<?> prefixTypeAttrKey = AttributeKey.valueOf(attr.prefix(), name);
attrKeys.add(prefixTypeAttrKey);

final AttributeKey<?> declaredTypeKey = AttributeKey.valueOf(type, name);
attrKeys.add(declaredTypeKey);

if (type.isPrimitive()) {
final Class<?> mustRefType = Primitives.wrap(type);
final AttributeKey<?> mustRefTypeKey = AttributeKey.valueOf(mustRefType, name);
attrKeys.add(mustRefTypeKey);
final ImmutableList.Builder<AttributeKey<?>> builder = ImmutableList.builder();

if (attr.prefix() != DefaultValues.class) {
// If @Attribute#prefix are defined, use only it to resolve Parameter.
builder.add(AttributeKey.valueOf(attr.prefix(), name));
}
else {
// If @Attribute@preifx are undefined, search in order of priority 1-3.
// Pri 1. ServiceName
final Class<?> serviceClass = ((Parameter) annotatedElement).getDeclaringExecutable()
.getDeclaringClass();
builder.add(AttributeKey.valueOf(serviceClass, name));
// Pri 2. Parameter type.
builder.add(AttributeKey.valueOf(type, name));

if (type.isPrimitive()) {
// Pri 2+a, Reference Type of Parameter in case of Parameter is primitive type.
final Class<?> mustRefType = Primitives.wrap(type);
builder.add(AttributeKey.valueOf(mustRefType, name));
}
// Pri 3, Object Type.
builder.add(AttributeKey.valueOf(name));
}

final AttributeKey<Object> objectTypeAttrKey = AttributeKey.valueOf(name);
attrKeys.add(objectTypeAttrKey);

final ImmutableList<AttributeKey<?>> attrKeys = builder.build();
return new Builder(annotatedElement, type, name)
.annotationType(Attribute.class)
.typeElement(typeElement)
.supportDefault(true)
.supportContainer(true)
.description(description)
.resolver(attributeResolver(ctx -> {
for (AttributeKey<?> attrKey : attrKeys) {
final Object value = ctx.context.attr(attrKey);
if (value != null)
return value;
}
return null;
}))
.resolver(attributeResolver(attrKeys))
.build();
}

Expand Down Expand Up @@ -828,12 +828,18 @@ private static AnnotatedValueResolver ofInjectableTypes0(String name, AnnotatedE
};
}

/**
* Returns a collection value resolver which retrieves a list of string from the specified {@code getter}
* AttributeKeys {@code ofAttribute} and adds them to the specified collection data type.
*/
private static BiFunction<AnnotatedValueResolver, ResolverContext, Object>
attributeResolver(Function<ResolverContext, Object> getter) {
attributeResolver(Iterable<AttributeKey<?>> attrKeys) {
return (resolver, ctx) -> {
Object value = getter.apply(ctx);
if (value != null) {
return value;
for (AttributeKey<?> attrKey : attrKeys) {
final Object value = ctx.context.attr(attrKey);
if (value != null) {
return value;
}
}
return resolver.defaultOrException();
};
Expand Down
Loading

0 comments on commit 42ef9a2

Please sign in to comment.