/*
* Copyright 2002-2016 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
*
* http://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.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesContextConfig;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/**
* Unit tests for {@link AnnotationAttributes}.
*
* @author Chris Beams
* @author Sam Brannen
* @author Juergen Hoeller
* @since 3.1.1
*/
public class AnnotationAttributesTests {
private AnnotationAttributes attributes = new AnnotationAttributes();
@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void typeSafeAttributeAccess() {
AnnotationAttributes nestedAttributes = new AnnotationAttributes();
nestedAttributes.put("value", 10);
nestedAttributes.put("name", "algernon");
attributes.put("name", "dave");
attributes.put("names", new String[] {"dave", "frank", "hal"});
attributes.put("bool1", true);
attributes.put("bool2", false);
attributes.put("color", Color.RED);
attributes.put("class", Integer.class);
attributes.put("classes", new Class<?>[] {Number.class, Short.class, Integer.class});
attributes.put("number", 42);
attributes.put("anno", nestedAttributes);
attributes.put("annoArray", new AnnotationAttributes[] {nestedAttributes});
assertThat(attributes.getString("name"), equalTo("dave"));
assertThat(attributes.getStringArray("names"), equalTo(new String[] {"dave", "frank", "hal"}));
assertThat(attributes.getBoolean("bool1"), equalTo(true));
assertThat(attributes.getBoolean("bool2"), equalTo(false));
assertThat(attributes.<Color>getEnum("color"), equalTo(Color.RED));
assertTrue(attributes.getClass("class").equals(Integer.class));
assertThat(attributes.getClassArray("classes"), equalTo(new Class<?>[] {Number.class, Short.class, Integer.class}));
assertThat(attributes.<Integer>getNumber("number"), equalTo(42));
assertThat(attributes.getAnnotation("anno").<Integer>getNumber("value"), equalTo(10));
assertThat(attributes.getAnnotationArray("annoArray")[0].getString("name"), equalTo("algernon"));
}
@Test
public void unresolvableClass() throws Exception {
attributes.put("unresolvableClass", new ClassNotFoundException("myclass"));
exception.expect(IllegalArgumentException.class);
exception.expectMessage(containsString("myclass"));
attributes.getClass("unresolvableClass");
}
@Test
public void singleElementToSingleElementArrayConversionSupport() throws Exception {
Filter filter = FilteredClass.class.getAnnotation(Filter.class);
AnnotationAttributes nestedAttributes = new AnnotationAttributes();
nestedAttributes.put("name", "Dilbert");
// Store single elements
attributes.put("names", "Dogbert");
attributes.put("classes", Number.class);
attributes.put("nestedAttributes", nestedAttributes);
attributes.put("filters", filter);
// Get back arrays of single elements
assertThat(attributes.getStringArray("names"), equalTo(new String[] {"Dogbert"}));
assertThat(attributes.getClassArray("classes"), equalTo(new Class<?>[] {Number.class}));
AnnotationAttributes[] array = attributes.getAnnotationArray("nestedAttributes");
assertNotNull(array);
assertThat(array.length, is(1));
assertThat(array[0].getString("name"), equalTo("Dilbert"));
Filter[] filters = attributes.getAnnotationArray("filters", Filter.class);
assertNotNull(filters);
assertThat(filters.length, is(1));
assertThat(filters[0].pattern(), equalTo("foo"));
}
@Test
public void nestedAnnotations() throws Exception {
Filter filter = FilteredClass.class.getAnnotation(Filter.class);
attributes.put("filter", filter);
attributes.put("filters", new Filter[] {filter, filter});
Filter retrievedFilter = attributes.getAnnotation("filter", Filter.class);
assertThat(retrievedFilter, equalTo(filter));
assertThat(retrievedFilter.pattern(), equalTo("foo"));
Filter[] retrievedFilters = attributes.getAnnotationArray("filters", Filter.class);
assertNotNull(retrievedFilters);
assertEquals(2, retrievedFilters.length);
assertThat(retrievedFilters[1].pattern(), equalTo("foo"));
}
@Test
public void getEnumWithNullAttributeName() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("must not be null or empty");
attributes.getEnum(null);
}
@Test
public void getEnumWithEmptyAttributeName() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("must not be null or empty");
attributes.getEnum("");
}
@Test
public void getEnumWithUnknownAttributeName() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Attribute 'bogus' not found");
attributes.getEnum("bogus");
}
@Test
public void getEnumWithTypeMismatch() {
attributes.put("color", "RED");
exception.expect(IllegalArgumentException.class);
exception.expectMessage(containsString("Attribute 'color' is of type [String], but [Enum] was expected"));
attributes.getEnum("color");
}
@Test
public void getAliasedStringWithImplicitAliases() {
String value = "metaverse";
List<String> aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript");
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("value", value);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertEquals(value, attributes.getString(alias)));
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("location1", value);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertEquals(value, attributes.getString(alias)));
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("value", value);
attributes.put("location1", value);
attributes.put("xmlFile", value);
attributes.put("groovyScript", value);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertEquals(value, attributes.getString(alias)));
}
@Test
public void getAliasedStringArrayWithImplicitAliases() {
String[] value = new String[] {"test.xml"};
List<String> aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript");
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("location1", value);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias)));
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("value", value);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias)));
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("location1", value);
attributes.put("value", value);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias)));
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("location1", value);
AnnotationUtils.registerDefaultValues(attributes);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias)));
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
attributes.put("value", value);
AnnotationUtils.registerDefaultValues(attributes);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias)));
attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class);
AnnotationUtils.registerDefaultValues(attributes);
AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false);
aliases.stream().forEach(alias -> assertArrayEquals(new String[] {""}, attributes.getStringArray(alias)));
}
enum Color {
RED, WHITE, BLUE
}
@Retention(RetentionPolicy.RUNTIME)
@interface Filter {
@AliasFor(attribute = "classes")
Class<?>[] value() default {};
@AliasFor(attribute = "value")
Class<?>[] classes() default {};
String pattern();
}
@Filter(pattern = "foo")
static class FilteredClass {
}
}