Refine unmapped property support

Replace `@UnmappedPropertyValue` with a `skip` attribute on
`@PropertyMapping` so that any default attribute value can be skipped.

Closes gh-6455
pull/6486/merge
Phillip Webb 8 years ago
parent ac1ad2a145
commit 5d9836b3b1

@ -17,7 +17,6 @@
package org.springframework.boot.test.autoconfigure.properties; package org.springframework.boot.test.autoconfigure.properties;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -110,33 +109,32 @@ public class AnnotationsPropertySource extends EnumerablePropertySource<Class<?>
PropertyMapping typeMapping, Map<String, Object> properties) { PropertyMapping typeMapping, Map<String, Object> properties) {
PropertyMapping attributeMapping = AnnotationUtils.getAnnotation(attribute, PropertyMapping attributeMapping = AnnotationUtils.getAnnotation(attribute,
PropertyMapping.class); PropertyMapping.class);
if (isMapped(typeMapping, attributeMapping)) { SkipPropertyMapping skip = getMappingType(typeMapping, attributeMapping);
if (skip == SkipPropertyMapping.YES) {
return;
}
String name = getName(typeMapping, attributeMapping, attribute); String name = getName(typeMapping, attributeMapping, attribute);
ReflectionUtils.makeAccessible(attribute); ReflectionUtils.makeAccessible(attribute);
Object value = ReflectionUtils.invokeMethod(attribute, annotation); Object value = ReflectionUtils.invokeMethod(attribute, annotation);
if (isValueMapped(value)) { if (skip == SkipPropertyMapping.ON_DEFAULT_VALUE) {
putProperties(name, value, properties); Object defaultValue = AnnotationUtils.getDefaultValue(annotation,
attribute.getName());
if (ObjectUtils.nullSafeEquals(value, defaultValue)) {
return;
} }
} }
putProperties(name, value, properties);
} }
private boolean isMapped(PropertyMapping typeMapping, private SkipPropertyMapping getMappingType(PropertyMapping typeMapping,
PropertyMapping attributeMapping) { PropertyMapping attributeMapping) {
if (attributeMapping != null) { if (attributeMapping != null) {
return attributeMapping.map(); return attributeMapping.skip();
}
return (typeMapping != null && typeMapping.map());
} }
if (typeMapping != null) {
private boolean isValueMapped(Object value) { return typeMapping.skip();
if (value != null && value instanceof Enum) {
Field field = ReflectionUtils.findField(value.getClass(),
((Enum<?>) value).name());
if (AnnotatedElementUtils.isAnnotated(field, UnmappedPropertyValue.class)) {
return false;
} }
} return SkipPropertyMapping.YES;
return true;
} }
private String getName(PropertyMapping typeMapping, PropertyMapping attributeMapping, private String getName(PropertyMapping typeMapping, PropertyMapping attributeMapping,

@ -47,7 +47,6 @@ import org.springframework.test.context.TestPropertySource;
* @author Phillip Webb * @author Phillip Webb
* @since 1.4.0 * @since 1.4.0
* @see AnnotationsPropertySource * @see AnnotationsPropertySource
* @see UnmappedPropertyValue
* @see TestPropertySource * @see TestPropertySource
*/ */
@Documented @Documented
@ -64,11 +63,11 @@ public @interface PropertyMapping {
String value() default ""; String value() default "";
/** /**
* Determines if mapping should occur. When specified at the type-level indicates if * Determines if mapping should be skipped. When specified at the type-level indicates
* mapping should occur by default or not. When used at the attribute-level, overrides * if skipping should occur by default or not. When used at the attribute-level,
* the type-level default. * overrides the type-level default.
* @return if mapping should occur * @return if mapping should be skipped
*/ */
boolean map() default true; SkipPropertyMapping skip() default SkipPropertyMapping.NO;
} }

@ -16,21 +16,27 @@
package org.springframework.boot.test.autoconfigure.properties; package org.springframework.boot.test.autoconfigure.properties;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* Indicates that a single value should not be mapped when referenced from a * Enum used to control when {@link PropertyMapping} is skipped.
* {@link PropertyMapping @PropertyMapping} annotation.
* *
* @author Phillip Webb * @author Phillip Webb
* @since 1.4.0 * @since 1.4.0
* @see PropertyMapping
*/ */
@Retention(RetentionPolicy.RUNTIME) public enum SkipPropertyMapping {
@Target({ ElementType.FIELD })
public @interface UnmappedPropertyValue { /**
* Skip the mapping the property.
*/
YES,
/**
* Skip mapping the property when the default attribute value is specified.
*/
ON_DEFAULT_VALUE,
/**
* Don't skip mapping the property.
*/
NO,
} }

@ -27,6 +27,7 @@ import org.openqa.selenium.WebDriver;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping; import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.test.autoconfigure.properties.SkipPropertyMapping;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
@ -57,6 +58,7 @@ public @interface AutoConfigureMockMvc {
* How {@link MvcResult} information should be printed after each MockMVC invocation. * How {@link MvcResult} information should be printed after each MockMVC invocation.
* @return how information is printed * @return how information is printed
*/ */
@PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)
MockMvcPrint print() default MockMvcPrint.DEFAULT; MockMvcPrint print() default MockMvcPrint.DEFAULT;
/** /**

@ -16,8 +16,6 @@
package org.springframework.boot.test.autoconfigure.web.servlet; package org.springframework.boot.test.autoconfigure.web.servlet;
import org.springframework.boot.test.autoconfigure.properties.UnmappedPropertyValue;
/** /**
* MVC print options specified from {@link AutoConfigureMockMvc}. * MVC print options specified from {@link AutoConfigureMockMvc}.
* *
@ -30,7 +28,7 @@ public enum MockMvcPrint {
* Use the default print setting ({@code MockMvcPrint.SYSTEM_OUT} unless explicitly * Use the default print setting ({@code MockMvcPrint.SYSTEM_OUT} unless explicitly
* overridden). * overridden).
*/ */
@UnmappedPropertyValue DEFAULT, DEFAULT,
/** /**
* Log MVC interactions at the {@code DEBUG} level. * Log MVC interactions at the {@code DEBUG} level.

@ -262,7 +262,7 @@ public class AnnotationsPropertySourceTests {
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@PropertyMapping(map = false) @PropertyMapping(skip = SkipPropertyMapping.YES)
static @interface NotMappedAtTypeLevelAnnotation { static @interface NotMappedAtTypeLevelAnnotation {
@PropertyMapping @PropertyMapping
@ -283,7 +283,7 @@ public class AnnotationsPropertySourceTests {
String value(); String value();
@PropertyMapping(map = false) @PropertyMapping(skip = SkipPropertyMapping.YES)
String ignore() default "xyz"; String ignore() default "xyz";
} }
@ -362,7 +362,9 @@ public class AnnotationsPropertySourceTests {
static @interface AttributeWithAliasAnnotation { static @interface AttributeWithAliasAnnotation {
@AliasFor(annotation = AliasedAttributeAnnotation.class, attribute = "value") @AliasFor(annotation = AliasedAttributeAnnotation.class, attribute = "value")
String value() default "foo"; String value()
default "foo";
String someOtherAttribute() default "shouldNotBeMapped"; String someOtherAttribute() default "shouldNotBeMapped";
@ -410,13 +412,14 @@ public class AnnotationsPropertySourceTests {
@PropertyMapping("testenum") @PropertyMapping("testenum")
static @interface EnumAnnotation { static @interface EnumAnnotation {
EnumItem value(); @PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)
EnumItem value() default EnumItem.DEFAULT;
} }
enum EnumItem { enum EnumItem {
@UnmappedPropertyValue DEFAULT, DEFAULT,
ONE, ONE,

Loading…
Cancel
Save