All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.springframework.core.annotation.MapAnnotationAttributeExtractor Maven / Gradle / Ivy
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Implementation of the {@link AnnotationAttributeExtractor} strategy that
* is backed by a {@link Map}.
*
* @author Sam Brannen
* @since 4.2
* @see Annotation
* @see AliasFor
* @see AbstractAliasAwareAnnotationAttributeExtractor
* @see DefaultAnnotationAttributeExtractor
* @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)
*/
class MapAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttributeExtractor> {
/**
* Construct a new {@code MapAnnotationAttributeExtractor}.
* The supplied map must contain a key-value pair for every attribute
* defined in the supplied {@code annotationType} that is not aliased or
* does not have a default value.
* @param attributes the map of annotation attributes; never {@code null}
* @param annotationType the type of annotation to synthesize; never {@code null}
* @param annotatedElement the element that is annotated with the annotation
* of the supplied type; may be {@code null} if unknown
*/
MapAnnotationAttributeExtractor(Map attributes, Class annotationType,
@Nullable AnnotatedElement annotatedElement) {
super(annotationType, annotatedElement, enrichAndValidateAttributes(attributes, annotationType));
}
@Override
@Nullable
protected Object getRawAttributeValue(Method attributeMethod) {
return getRawAttributeValue(attributeMethod.getName());
}
@Override
@Nullable
protected Object getRawAttributeValue(String attributeName) {
return getSource().get(attributeName);
}
/**
* Enrich and validate the supplied attributes map by ensuring
* that it contains a non-null entry for each annotation attribute in
* the specified {@code annotationType} and that the type of the entry
* matches the return type for the corresponding annotation attribute.
* If an entry is a map (presumably of annotation attributes), an
* attempt will be made to synthesize an annotation from it. Similarly,
* if an entry is an array of maps, an attempt will be made to synthesize
* an array of annotations from those maps.
*
If an attribute is missing in the supplied map, it will be set
* either to the value of its alias (if an alias exists) or to the
* value of the attribute's default value (if defined), and otherwise
* an {@link IllegalArgumentException} will be thrown.
*/
@SuppressWarnings("unchecked")
private static Map enrichAndValidateAttributes(
Map originalAttributes, Class annotationType) {
Map attributes = new LinkedHashMap<>(originalAttributes);
Map> attributeAliasMap = AnnotationUtils.getAttributeAliasMap(annotationType);
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType)) {
String attributeName = attributeMethod.getName();
Object attributeValue = attributes.get(attributeName);
// if attribute not present, check aliases
if (attributeValue == null) {
List aliasNames = attributeAliasMap.get(attributeName);
if (aliasNames != null) {
for (String aliasName : aliasNames) {
Object aliasValue = attributes.get(aliasName);
if (aliasValue != null) {
attributeValue = aliasValue;
attributes.put(attributeName, attributeValue);
break;
}
}
}
}
// if aliases not present, check default
if (attributeValue == null) {
Object defaultValue = AnnotationUtils.getDefaultValue(annotationType, attributeName);
if (defaultValue != null) {
attributeValue = defaultValue;
attributes.put(attributeName, attributeValue);
}
}
// if still null
Assert.notNull(attributeValue, () -> String.format(
"Attributes map %s returned null for required attribute '%s' defined by annotation type [%s].",
attributes, attributeName, annotationType.getName()));
// finally, ensure correct type
Class requiredReturnType = attributeMethod.getReturnType();
Class actualReturnType = attributeValue.getClass();
if (!ClassUtils.isAssignable(requiredReturnType, actualReturnType)) {
boolean converted = false;
// Single element overriding an array of the same type?
if (requiredReturnType.isArray() && requiredReturnType.getComponentType() == actualReturnType) {
Object array = Array.newInstance(requiredReturnType.getComponentType(), 1);
Array.set(array, 0, attributeValue);
attributes.put(attributeName, array);
converted = true;
}
// Nested map representing a single annotation?
else if (Annotation.class.isAssignableFrom(requiredReturnType) &&
Map.class.isAssignableFrom(actualReturnType)) {
Class nestedAnnotationType =
(Class) requiredReturnType;
Map map = (Map) attributeValue;
attributes.put(attributeName, AnnotationUtils.synthesizeAnnotation(map, nestedAnnotationType, null));
converted = true;
}
// Nested array of maps representing an array of annotations?
else if (requiredReturnType.isArray() && actualReturnType.isArray() &&
Annotation.class.isAssignableFrom(requiredReturnType.getComponentType()) &&
Map.class.isAssignableFrom(actualReturnType.getComponentType())) {
Class nestedAnnotationType =
(Class) requiredReturnType.getComponentType();
Map[] maps = (Map[]) attributeValue;
attributes.put(attributeName, AnnotationUtils.synthesizeAnnotationArray(maps, nestedAnnotationType));
converted = true;
}
Assert.isTrue(converted, () -> String.format(
"Attributes map %s returned a value of type [%s] for attribute '%s', " +
"but a value of type [%s] is required as defined by annotation type [%s].",
attributes, actualReturnType.getName(), attributeName, requiredReturnType.getName(),
annotationType.getName()));
}
}
return attributes;
}
}