All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.core.annotation.MapAnnotationAttributeExtractor Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy