Polish 'Generate configuration metadata for records'
Restructure `PropertyDescriptor` type hierarchy and polish code. See gh-29403
This commit is contained in:
@@ -16,209 +16,47 @@
|
||||
|
||||
package org.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.RecordComponentElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.TypeKindVisitor8;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
|
||||
/**
|
||||
* A {@link PropertyDescriptor} for a constructor parameter.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Pavel Anisimov
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConstructorParameterPropertyDescriptor extends PropertyDescriptor<VariableElement> {
|
||||
class ConstructorParameterPropertyDescriptor extends ParameterPropertyDescriptor {
|
||||
|
||||
private final RecordComponentElement recordComponent;
|
||||
private final ExecutableElement setter;
|
||||
|
||||
ConstructorParameterPropertyDescriptor(TypeElement ownerElement, ExecutableElement factoryMethod,
|
||||
VariableElement source, String name, TypeMirror type, VariableElement field,
|
||||
RecordComponentElement recordComponent, ExecutableElement getter, ExecutableElement setter) {
|
||||
super(ownerElement, factoryMethod, source, name, type, field, getter, setter);
|
||||
this.recordComponent = recordComponent;
|
||||
private final VariableElement field;
|
||||
|
||||
ConstructorParameterPropertyDescriptor(String name, TypeMirror type, VariableElement parameter,
|
||||
TypeElement declaringElement, ExecutableElement getter, ExecutableElement setter, VariableElement field) {
|
||||
super(name, type, parameter, declaringElement, getter);
|
||||
this.setter = setter;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isProperty(MetadataGenerationEnvironment env) {
|
||||
// If it's a constructor parameter, it doesn't matter as we must be able to bind
|
||||
// it to build the object.
|
||||
return !isNested(env);
|
||||
protected List<Element> getDeprecatableElements() {
|
||||
return Arrays.asList(getGetter(), this.setter, this.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveDefaultValue(MetadataGenerationEnvironment environment) {
|
||||
Object defaultValue = getDefaultValueFromAnnotation(environment, getSource());
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getSource().asType().accept(DefaultPrimitiveTypeVisitor.INSTANCE, null);
|
||||
protected boolean isMarkedAsNested(MetadataGenerationEnvironment environment) {
|
||||
return environment.getNestedConfigurationPropertyAnnotation(this.field) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveDescription(MetadataGenerationEnvironment environment) {
|
||||
// record components descriptions are written using @param tag
|
||||
if (this.recordComponent != null) {
|
||||
return environment.getTypeUtils().getJavaDoc(this.recordComponent);
|
||||
}
|
||||
return super.resolveDescription(environment);
|
||||
}
|
||||
|
||||
private Object getDefaultValueFromAnnotation(MetadataGenerationEnvironment environment, Element element) {
|
||||
AnnotationMirror annotation = environment.getDefaultValueAnnotation(element);
|
||||
List<String> defaultValue = getDefaultValue(environment, annotation);
|
||||
if (defaultValue != null) {
|
||||
try {
|
||||
TypeMirror specificType = determineSpecificType(environment);
|
||||
if (defaultValue.size() == 1) {
|
||||
return coerceValue(specificType, defaultValue.get(0));
|
||||
}
|
||||
return defaultValue.stream().map((value) -> coerceValue(specificType, value)).toList();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
environment.getMessager().printMessage(Kind.ERROR, ex.getMessage(), element, annotation);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<String> getDefaultValue(MetadataGenerationEnvironment environment, AnnotationMirror annotation) {
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> values = environment.getAnnotationElementValues(annotation);
|
||||
return (List<String>) values.get("value");
|
||||
}
|
||||
|
||||
private TypeMirror determineSpecificType(MetadataGenerationEnvironment environment) {
|
||||
TypeMirror candidate = getSource().asType();
|
||||
TypeMirror elementCandidate = environment.getTypeUtils().extractElementType(candidate);
|
||||
if (elementCandidate != null) {
|
||||
candidate = elementCandidate;
|
||||
}
|
||||
PrimitiveType primitiveType = environment.getTypeUtils().getPrimitiveType(candidate);
|
||||
return (primitiveType != null) ? primitiveType : candidate;
|
||||
}
|
||||
|
||||
private Object coerceValue(TypeMirror type, String value) {
|
||||
Object coercedValue = type.accept(DefaultValueCoercionTypeVisitor.INSTANCE, value);
|
||||
return (coercedValue != null) ? coercedValue : value;
|
||||
}
|
||||
|
||||
private static final class DefaultValueCoercionTypeVisitor extends TypeKindVisitor8<Object, String> {
|
||||
|
||||
private static final DefaultValueCoercionTypeVisitor INSTANCE = new DefaultValueCoercionTypeVisitor();
|
||||
|
||||
private <T extends Number> T parseNumber(String value, Function<String, T> parser,
|
||||
PrimitiveType primitiveType) {
|
||||
try {
|
||||
return parser.apply(value);
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid %s representation '%s'", primitiveType, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsBoolean(PrimitiveType t, String value) {
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsByte(PrimitiveType t, String value) {
|
||||
return parseNumber(value, Byte::parseByte, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsShort(PrimitiveType t, String value) {
|
||||
return parseNumber(value, Short::parseShort, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsInt(PrimitiveType t, String value) {
|
||||
return parseNumber(value, Integer::parseInt, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsLong(PrimitiveType t, String value) {
|
||||
return parseNumber(value, Long::parseLong, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsChar(PrimitiveType t, String value) {
|
||||
if (value.length() > 1) {
|
||||
throw new IllegalArgumentException(String.format("Invalid character representation '%s'", value));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsFloat(PrimitiveType t, String value) {
|
||||
return parseNumber(value, Float::parseFloat, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsDouble(PrimitiveType t, String value) {
|
||||
return parseNumber(value, Double::parseDouble, t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class DefaultPrimitiveTypeVisitor extends TypeKindVisitor8<Object, Void> {
|
||||
|
||||
private static final DefaultPrimitiveTypeVisitor INSTANCE = new DefaultPrimitiveTypeVisitor();
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsBoolean(PrimitiveType t, Void ignore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsByte(PrimitiveType t, Void ignore) {
|
||||
return (byte) 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsShort(PrimitiveType t, Void ignore) {
|
||||
return (short) 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsInt(PrimitiveType t, Void ignore) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsLong(PrimitiveType t, Void ignore) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsChar(PrimitiveType t, Void ignore) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsFloat(PrimitiveType t, Void ignore) {
|
||||
return 0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsDouble(PrimitiveType t, Void ignore) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
return environment.getTypeUtils().getJavaDoc(this.field);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
@@ -25,23 +29,54 @@ import javax.lang.model.type.TypeMirror;
|
||||
* A {@link PropertyDescriptor} for a standard JavaBean property.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class JavaBeanPropertyDescriptor extends PropertyDescriptor<ExecutableElement> {
|
||||
class JavaBeanPropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
JavaBeanPropertyDescriptor(TypeElement ownerElement, ExecutableElement factoryMethod, ExecutableElement getter,
|
||||
String name, TypeMirror type, VariableElement field, ExecutableElement setter) {
|
||||
super(ownerElement, factoryMethod, getter, name, type, field, getter, setter);
|
||||
private final ExecutableElement setter;
|
||||
|
||||
private final VariableElement field;
|
||||
|
||||
private final ExecutableElement factoryMethod;
|
||||
|
||||
JavaBeanPropertyDescriptor(String name, TypeMirror type, TypeElement declaringElement, ExecutableElement getter,
|
||||
ExecutableElement setter, VariableElement field, ExecutableElement factoryMethod) {
|
||||
super(name, type, declaringElement, getter);
|
||||
this.setter = setter;
|
||||
this.field = field;
|
||||
this.factoryMethod = factoryMethod;
|
||||
}
|
||||
|
||||
ExecutableElement getSetter() {
|
||||
return this.setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isProperty(MetadataGenerationEnvironment env) {
|
||||
boolean isCollection = env.getTypeUtils().isCollectionOrMap(getType());
|
||||
return !env.isExcluded(getType()) && getGetter() != null && (getSetter() != null || isCollection);
|
||||
protected boolean isMarkedAsNested(MetadataGenerationEnvironment environment) {
|
||||
return environment.getNestedConfigurationPropertyAnnotation(this.field) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveDescription(MetadataGenerationEnvironment environment) {
|
||||
return environment.getTypeUtils().getJavaDoc(this.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveDefaultValue(MetadataGenerationEnvironment environment) {
|
||||
return environment.getFieldDefaultValue(getOwnerElement(), getName());
|
||||
return environment.getFieldDefaultValue(getDeclaringElement(), getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Element> getDeprecatableElements() {
|
||||
return Arrays.asList(getGetter(), this.setter, this.field, this.factoryMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty(MetadataGenerationEnvironment env) {
|
||||
boolean isCollection = env.getTypeUtils().isCollectionOrMap(getType());
|
||||
boolean hasGetter = getGetter() != null;
|
||||
boolean hasSetter = getSetter() != null;
|
||||
return !env.isExcluded(getType()) && hasGetter && (hasSetter || isCollection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -16,23 +16,25 @@
|
||||
|
||||
package org.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ItemDeprecation;
|
||||
|
||||
/**
|
||||
* A {@link PropertyDescriptor} for a Lombok field.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class LombokPropertyDescriptor extends PropertyDescriptor<VariableElement> {
|
||||
class LombokPropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
private static final String LOMBOK_DATA_ANNOTATION = "lombok.Data";
|
||||
|
||||
@@ -44,13 +46,46 @@ class LombokPropertyDescriptor extends PropertyDescriptor<VariableElement> {
|
||||
|
||||
private static final String LOMBOK_ACCESS_LEVEL_PUBLIC = "PUBLIC";
|
||||
|
||||
LombokPropertyDescriptor(TypeElement typeElement, ExecutableElement factoryMethod, VariableElement field,
|
||||
String name, TypeMirror type, ExecutableElement getter, ExecutableElement setter) {
|
||||
super(typeElement, factoryMethod, field, name, type, field, getter, setter);
|
||||
private final ExecutableElement setter;
|
||||
|
||||
private final VariableElement field;
|
||||
|
||||
private final ExecutableElement factoryMethod;
|
||||
|
||||
LombokPropertyDescriptor(String name, TypeMirror type, TypeElement declaringElement, ExecutableElement getter,
|
||||
ExecutableElement setter, VariableElement field, ExecutableElement factoryMethod) {
|
||||
super(name, type, declaringElement, getter);
|
||||
this.factoryMethod = factoryMethod;
|
||||
this.field = field;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
VariableElement getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isProperty(MetadataGenerationEnvironment env) {
|
||||
protected boolean isMarkedAsNested(MetadataGenerationEnvironment environment) {
|
||||
return environment.getNestedConfigurationPropertyAnnotation(getField()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveDescription(MetadataGenerationEnvironment environment) {
|
||||
return environment.getTypeUtils().getJavaDoc(this.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveDefaultValue(MetadataGenerationEnvironment environment) {
|
||||
return environment.getFieldDefaultValue(getDeclaringElement(), getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Element> getDeprecatableElements() {
|
||||
return Arrays.asList(getGetter(), this.setter, this.field, this.factoryMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty(MetadataGenerationEnvironment env) {
|
||||
if (!hasLombokPublicAccessor(env, true)) {
|
||||
return false;
|
||||
}
|
||||
@@ -59,29 +94,14 @@ class LombokPropertyDescriptor extends PropertyDescriptor<VariableElement> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveDefaultValue(MetadataGenerationEnvironment environment) {
|
||||
return environment.getFieldDefaultValue(getOwnerElement(), getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNested(MetadataGenerationEnvironment environment) {
|
||||
if (!hasLombokPublicAccessor(environment, true)) {
|
||||
return false;
|
||||
}
|
||||
return super.isNested(environment);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemDeprecation resolveItemDeprecation(MetadataGenerationEnvironment environment) {
|
||||
boolean deprecated = environment.isDeprecated(getField()) || environment.isDeprecated(getGetter())
|
||||
|| environment.isDeprecated(getFactoryMethod());
|
||||
return deprecated ? environment.resolveItemDeprecation(getGetter()) : null;
|
||||
public boolean isNested(MetadataGenerationEnvironment environment) {
|
||||
return hasLombokPublicAccessor(environment, true) && super.isNested(environment);
|
||||
}
|
||||
|
||||
private boolean hasSetter(MetadataGenerationEnvironment env) {
|
||||
boolean nonFinalPublicField = !getField().getModifiers().contains(Modifier.FINAL)
|
||||
&& hasLombokPublicAccessor(env, false);
|
||||
return getSetter() != null || nonFinalPublicField;
|
||||
return this.setter != null || nonFinalPublicField;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,12 +118,12 @@ class LombokPropertyDescriptor extends PropertyDescriptor<VariableElement> {
|
||||
if (lombokMethodAnnotationOnField != null) {
|
||||
return isAccessLevelPublic(env, lombokMethodAnnotationOnField);
|
||||
}
|
||||
AnnotationMirror lombokMethodAnnotationOnElement = env.getAnnotation(getOwnerElement(), annotation);
|
||||
AnnotationMirror lombokMethodAnnotationOnElement = env.getAnnotation(getDeclaringElement(), annotation);
|
||||
if (lombokMethodAnnotationOnElement != null) {
|
||||
return isAccessLevelPublic(env, lombokMethodAnnotationOnElement);
|
||||
}
|
||||
return (env.hasAnnotation(getOwnerElement(), LOMBOK_DATA_ANNOTATION)
|
||||
|| env.hasAnnotation(getOwnerElement(), LOMBOK_VALUE_ANNOTATION));
|
||||
return (env.hasAnnotation(getDeclaringElement(), LOMBOK_DATA_ANNOTATION)
|
||||
|| env.hasAnnotation(getDeclaringElement(), LOMBOK_VALUE_ANNOTATION));
|
||||
}
|
||||
|
||||
private boolean isAccessLevelPublic(MetadataGenerationEnvironment env, AnnotationMirror lombokAnnotation) {
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2012-2024 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.boot.configurationprocessor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.TypeKindVisitor8;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
|
||||
/**
|
||||
* {@link PropertyDescriptor} created from a constructor or record parameter.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class ParameterPropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
private final VariableElement parameter;
|
||||
|
||||
ParameterPropertyDescriptor(String name, TypeMirror type, VariableElement parameter, TypeElement declaringElement,
|
||||
ExecutableElement getter) {
|
||||
super(name, type, declaringElement, getter);
|
||||
this.parameter = parameter;
|
||||
|
||||
}
|
||||
|
||||
final VariableElement getParameter() {
|
||||
return this.parameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveDefaultValue(MetadataGenerationEnvironment environment) {
|
||||
Object defaultValue = getDefaultValueFromAnnotation(environment, getParameter());
|
||||
return (defaultValue != null) ? defaultValue
|
||||
: getParameter().asType().accept(DefaultPrimitiveTypeVisitor.INSTANCE, null);
|
||||
}
|
||||
|
||||
private Object getDefaultValueFromAnnotation(MetadataGenerationEnvironment environment, Element element) {
|
||||
AnnotationMirror annotation = environment.getDefaultValueAnnotation(element);
|
||||
List<String> defaultValue = getDefaultValue(environment, annotation);
|
||||
if (defaultValue != null) {
|
||||
TypeMirror specificType = determineSpecificType(environment);
|
||||
try {
|
||||
List<Object> coerced = defaultValue.stream().map((value) -> coerceValue(specificType, value)).toList();
|
||||
return (coerced.size() != 1) ? coerced : coerced.get(0);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
environment.getMessager().printMessage(Kind.ERROR, ex.getMessage(), element, annotation);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<String> getDefaultValue(MetadataGenerationEnvironment environment, AnnotationMirror annotation) {
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> values = environment.getAnnotationElementValues(annotation);
|
||||
return (List<String>) values.get("value");
|
||||
}
|
||||
|
||||
private TypeMirror determineSpecificType(MetadataGenerationEnvironment environment) {
|
||||
TypeMirror parameterType = getParameter().asType();
|
||||
TypeMirror elementType = environment.getTypeUtils().extractElementType(parameterType);
|
||||
parameterType = (elementType != null) ? elementType : parameterType;
|
||||
PrimitiveType primitiveType = environment.getTypeUtils().getPrimitiveType(parameterType);
|
||||
return (primitiveType != null) ? primitiveType : parameterType;
|
||||
}
|
||||
|
||||
private Object coerceValue(TypeMirror type, String value) {
|
||||
Object coercedValue = type.accept(DefaultValueCoercionTypeVisitor.INSTANCE, value);
|
||||
return (coercedValue != null) ? coercedValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty(MetadataGenerationEnvironment env) {
|
||||
return !isNested(env); // We must be able to bind it to build the object.
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor that gets the default value for primitives.
|
||||
*/
|
||||
private static final class DefaultPrimitiveTypeVisitor extends TypeKindVisitor8<Object, Void> {
|
||||
|
||||
static final DefaultPrimitiveTypeVisitor INSTANCE = new DefaultPrimitiveTypeVisitor();
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsBoolean(PrimitiveType type, Void parameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsByte(PrimitiveType type, Void parameter) {
|
||||
return (byte) 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsShort(PrimitiveType type, Void parameter) {
|
||||
return (short) 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsInt(PrimitiveType type, Void parameter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsLong(PrimitiveType type, Void parameter) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsChar(PrimitiveType type, Void parameter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsFloat(PrimitiveType type, Void parameter) {
|
||||
return 0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsDouble(PrimitiveType type, Void parameter) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor that gets the default using coercion.
|
||||
*/
|
||||
private static final class DefaultValueCoercionTypeVisitor extends TypeKindVisitor8<Object, String> {
|
||||
|
||||
static final DefaultValueCoercionTypeVisitor INSTANCE = new DefaultValueCoercionTypeVisitor();
|
||||
|
||||
private <T extends Number> T parseNumber(String value, Function<String, T> parser,
|
||||
PrimitiveType primitiveType) {
|
||||
try {
|
||||
return parser.apply(value);
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid %s representation '%s'", primitiveType, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsBoolean(PrimitiveType type, String value) {
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsByte(PrimitiveType type, String value) {
|
||||
return parseNumber(value, Byte::parseByte, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsShort(PrimitiveType type, String value) {
|
||||
return parseNumber(value, Short::parseShort, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsInt(PrimitiveType type, String value) {
|
||||
return parseNumber(value, Integer::parseInt, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsLong(PrimitiveType type, String value) {
|
||||
return parseNumber(value, Long::parseLong, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsChar(PrimitiveType type, String value) {
|
||||
if (value.length() > 1) {
|
||||
throw new IllegalArgumentException(String.format("Invalid character representation '%s'", value));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsFloat(PrimitiveType type, String value) {
|
||||
return parseNumber(value, Float::parseFloat, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitPrimitiveAsDouble(PrimitiveType type, String value) {
|
||||
return parseNumber(value, Double::parseDouble, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package org.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||
@@ -30,134 +31,107 @@ import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
|
||||
/**
|
||||
* Description of a property that can be candidate for metadata generation.
|
||||
*
|
||||
* @param <S> the type of the source element that determines the property
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class PropertyDescriptor<S extends Element> {
|
||||
|
||||
private final TypeElement ownerElement;
|
||||
|
||||
private final ExecutableElement factoryMethod;
|
||||
|
||||
private final S source;
|
||||
abstract class PropertyDescriptor {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final TypeMirror type;
|
||||
|
||||
private final VariableElement field;
|
||||
private final TypeElement declaringElement;
|
||||
|
||||
private final ExecutableElement getter;
|
||||
|
||||
private final ExecutableElement setter;
|
||||
|
||||
protected PropertyDescriptor(TypeElement ownerElement, ExecutableElement factoryMethod, S source, String name,
|
||||
TypeMirror type, VariableElement field, ExecutableElement getter, ExecutableElement setter) {
|
||||
this.ownerElement = ownerElement;
|
||||
this.factoryMethod = factoryMethod;
|
||||
this.source = source;
|
||||
/**
|
||||
* Create a new {@link PropertyDescriptor} instance.
|
||||
* @param name the property name
|
||||
* @param type the property type
|
||||
* @param declaringElement the element that declared the item
|
||||
* @param getter the getter for the property or {@code null}
|
||||
*/
|
||||
PropertyDescriptor(String name, TypeMirror type, TypeElement declaringElement, ExecutableElement getter) {
|
||||
this.declaringElement = declaringElement;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.field = field;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
TypeElement getOwnerElement() {
|
||||
return this.ownerElement;
|
||||
}
|
||||
|
||||
ExecutableElement getFactoryMethod() {
|
||||
return this.factoryMethod;
|
||||
}
|
||||
|
||||
S getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the property.
|
||||
* @return the property name
|
||||
*/
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of the property.
|
||||
* @return the property type
|
||||
*/
|
||||
TypeMirror getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
VariableElement getField() {
|
||||
return this.field;
|
||||
/**
|
||||
* Return the element that declared the property.
|
||||
* @return the declaring element
|
||||
*/
|
||||
protected final TypeElement getDeclaringElement() {
|
||||
return this.declaringElement;
|
||||
}
|
||||
|
||||
ExecutableElement getGetter() {
|
||||
/**
|
||||
* Return the getter for the property.
|
||||
* @return the getter or {@code null}
|
||||
*/
|
||||
protected final ExecutableElement getGetter() {
|
||||
return this.getter;
|
||||
}
|
||||
|
||||
ExecutableElement getSetter() {
|
||||
return this.setter;
|
||||
}
|
||||
|
||||
protected abstract boolean isProperty(MetadataGenerationEnvironment environment);
|
||||
|
||||
protected abstract Object resolveDefaultValue(MetadataGenerationEnvironment environment);
|
||||
|
||||
protected ItemDeprecation resolveItemDeprecation(MetadataGenerationEnvironment environment) {
|
||||
boolean deprecated = environment.isDeprecated(getGetter()) || environment.isDeprecated(getSetter())
|
||||
|| environment.isDeprecated(getField()) || environment.isDeprecated(getFactoryMethod());
|
||||
return deprecated ? environment.resolveItemDeprecation(getGetter()) : null;
|
||||
}
|
||||
|
||||
protected boolean isNested(MetadataGenerationEnvironment environment) {
|
||||
Element typeElement = environment.getTypeUtils().asElement(getType());
|
||||
if (!(typeElement instanceof TypeElement) || typeElement.getKind() == ElementKind.ENUM) {
|
||||
return false;
|
||||
}
|
||||
if (environment.getConfigurationPropertiesAnnotation(getGetter()) != null) {
|
||||
return false;
|
||||
}
|
||||
if (environment.getNestedConfigurationPropertyAnnotation(getField()) != null) {
|
||||
return true;
|
||||
}
|
||||
if (isCyclePresent(typeElement, getOwnerElement())) {
|
||||
return false;
|
||||
}
|
||||
return isParentTheSame(environment, typeElement, getOwnerElement());
|
||||
}
|
||||
|
||||
ItemMetadata resolveItemMetadata(String prefix, MetadataGenerationEnvironment environment) {
|
||||
/**
|
||||
* Resolve the {@link ItemMetadata} for this property.
|
||||
* @param prefix the property prefix
|
||||
* @param environment the metadata generation environment
|
||||
* @return the item metadata or {@code null}
|
||||
*/
|
||||
final ItemMetadata resolveItemMetadata(String prefix, MetadataGenerationEnvironment environment) {
|
||||
if (isNested(environment)) {
|
||||
return resolveItemMetadataGroup(prefix, environment);
|
||||
}
|
||||
else if (isProperty(environment)) {
|
||||
if (isProperty(environment)) {
|
||||
return resolveItemMetadataProperty(prefix, environment);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ItemMetadata resolveItemMetadataProperty(String prefix, MetadataGenerationEnvironment environment) {
|
||||
String dataType = resolveType(environment);
|
||||
String ownerType = environment.getTypeUtils().getQualifiedName(getOwnerElement());
|
||||
String description = resolveDescription(environment);
|
||||
Object defaultValue = resolveDefaultValue(environment);
|
||||
ItemDeprecation deprecation = resolveItemDeprecation(environment);
|
||||
return ItemMetadata.newProperty(prefix, getName(), dataType, ownerType, null, description, defaultValue,
|
||||
deprecation);
|
||||
/**
|
||||
* Return if this is a nested property.
|
||||
* @param environment the metadata generation environment
|
||||
* @return if the property is nested
|
||||
* @see #isMarkedAsNested(MetadataGenerationEnvironment)
|
||||
*/
|
||||
boolean isNested(MetadataGenerationEnvironment environment) {
|
||||
Element typeElement = environment.getTypeUtils().asElement(getType());
|
||||
if (!(typeElement instanceof TypeElement) || typeElement.getKind() == ElementKind.ENUM
|
||||
|| environment.getConfigurationPropertiesAnnotation(getGetter()) != null) {
|
||||
return false;
|
||||
}
|
||||
if (isMarkedAsNested(environment)) {
|
||||
return true;
|
||||
}
|
||||
return !isCyclePresent(typeElement, getDeclaringElement())
|
||||
&& isParentTheSame(environment, typeElement, getDeclaringElement());
|
||||
}
|
||||
|
||||
private ItemMetadata resolveItemMetadataGroup(String prefix, MetadataGenerationEnvironment environment) {
|
||||
Element propertyElement = environment.getTypeUtils().asElement(getType());
|
||||
String nestedPrefix = ConfigurationMetadata.nestedPrefix(prefix, getName());
|
||||
String dataType = environment.getTypeUtils().getQualifiedName(propertyElement);
|
||||
String ownerType = environment.getTypeUtils().getQualifiedName(getOwnerElement());
|
||||
String sourceMethod = (getGetter() != null) ? getGetter().toString() : null;
|
||||
return ItemMetadata.newGroup(nestedPrefix, dataType, ownerType, sourceMethod);
|
||||
}
|
||||
|
||||
private String resolveType(MetadataGenerationEnvironment environment) {
|
||||
return environment.getTypeUtils().getType(getOwnerElement(), getType());
|
||||
}
|
||||
|
||||
protected String resolveDescription(MetadataGenerationEnvironment environment) {
|
||||
return environment.getTypeUtils().getJavaDoc(getField());
|
||||
}
|
||||
/**
|
||||
* Return if this property has been explicitly marked as nested (for example using an
|
||||
* annotation}.
|
||||
* @param environment the metadata generation environment
|
||||
* @return if the property has been marked as nested
|
||||
*/
|
||||
protected abstract boolean isMarkedAsNested(MetadataGenerationEnvironment environment);
|
||||
|
||||
private boolean isCyclePresent(Element returnType, Element element) {
|
||||
if (!(element.getEnclosingElement() instanceof TypeElement)) {
|
||||
@@ -192,4 +166,60 @@ abstract class PropertyDescriptor<S extends Element> {
|
||||
return getTopLevelType(element.getEnclosingElement());
|
||||
}
|
||||
|
||||
private ItemMetadata resolveItemMetadataGroup(String prefix, MetadataGenerationEnvironment environment) {
|
||||
Element propertyElement = environment.getTypeUtils().asElement(getType());
|
||||
String nestedPrefix = ConfigurationMetadata.nestedPrefix(prefix, getName());
|
||||
String dataType = environment.getTypeUtils().getQualifiedName(propertyElement);
|
||||
String ownerType = environment.getTypeUtils().getQualifiedName(getDeclaringElement());
|
||||
String sourceMethod = (getGetter() != null) ? getGetter().toString() : null;
|
||||
return ItemMetadata.newGroup(nestedPrefix, dataType, ownerType, sourceMethod);
|
||||
}
|
||||
|
||||
private ItemMetadata resolveItemMetadataProperty(String prefix, MetadataGenerationEnvironment environment) {
|
||||
String dataType = resolveType(environment);
|
||||
String ownerType = environment.getTypeUtils().getQualifiedName(getDeclaringElement());
|
||||
String description = resolveDescription(environment);
|
||||
Object defaultValue = resolveDefaultValue(environment);
|
||||
ItemDeprecation deprecation = resolveItemDeprecation(environment);
|
||||
return ItemMetadata.newProperty(prefix, getName(), dataType, ownerType, null, description, defaultValue,
|
||||
deprecation);
|
||||
}
|
||||
|
||||
private String resolveType(MetadataGenerationEnvironment environment) {
|
||||
return environment.getTypeUtils().getType(getDeclaringElement(), getType());
|
||||
}
|
||||
|
||||
private ItemDeprecation resolveItemDeprecation(MetadataGenerationEnvironment environment) {
|
||||
boolean deprecated = getDeprecatableElements().stream().anyMatch(environment::isDeprecated);
|
||||
return deprecated ? environment.resolveItemDeprecation(getGetter()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the property description.
|
||||
* @param environment the metadata generation environment
|
||||
* @return the property description
|
||||
*/
|
||||
protected abstract String resolveDescription(MetadataGenerationEnvironment environment);
|
||||
|
||||
/**
|
||||
* Resolve the default value for this property.
|
||||
* @param environment the metadata generation environment
|
||||
* @return the default value or {@code null}
|
||||
*/
|
||||
protected abstract Object resolveDefaultValue(MetadataGenerationEnvironment environment);
|
||||
|
||||
/**
|
||||
* Return all the elements that should be considered when checking for deprecation
|
||||
* annotations.
|
||||
* @return the deprecatable elements
|
||||
*/
|
||||
protected abstract List<Element> getDeprecatableElements();
|
||||
|
||||
/**
|
||||
* Return true if this descriptor is for a property.
|
||||
* @param environment the metadata generation environment
|
||||
* @return if this is a property
|
||||
*/
|
||||
abstract boolean isProperty(MetadataGenerationEnvironment environment);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -56,41 +56,48 @@ class PropertyDescriptorResolver {
|
||||
* or {@code null}
|
||||
* @return the candidate properties for metadata generation
|
||||
*/
|
||||
Stream<PropertyDescriptor<?>> resolve(TypeElement type, ExecutableElement factoryMethod) {
|
||||
Stream<PropertyDescriptor> resolve(TypeElement type, ExecutableElement factoryMethod) {
|
||||
TypeElementMembers members = new TypeElementMembers(this.environment, type);
|
||||
if (factoryMethod != null) {
|
||||
return resolveJavaBeanProperties(type, factoryMethod, members);
|
||||
return resolveJavaBeanProperties(type, members, factoryMethod);
|
||||
}
|
||||
return resolve(ConfigurationPropertiesTypeElement.of(type, this.environment), members);
|
||||
return resolve(Bindable.of(type, this.environment), members);
|
||||
}
|
||||
|
||||
private Stream<PropertyDescriptor<?>> resolve(ConfigurationPropertiesTypeElement type, TypeElementMembers members) {
|
||||
if (type.isConstructorBindingEnabled()) {
|
||||
ExecutableElement constructor = type.getBindConstructor();
|
||||
if (constructor != null) {
|
||||
return resolveConstructorProperties(type.getType(), members, constructor);
|
||||
}
|
||||
return Stream.empty();
|
||||
private Stream<PropertyDescriptor> resolve(Bindable bindable, TypeElementMembers members) {
|
||||
if (bindable.isConstructorBindingEnabled()) {
|
||||
ExecutableElement bindConstructor = bindable.getBindConstructor();
|
||||
return (bindConstructor != null)
|
||||
? resolveConstructorBoundProperties(bindable.getType(), members, bindConstructor) : Stream.empty();
|
||||
}
|
||||
return resolveJavaBeanProperties(type.getType(), null, members);
|
||||
return resolveJavaBeanProperties(bindable.getType(), members, null);
|
||||
}
|
||||
|
||||
Stream<PropertyDescriptor<?>> resolveConstructorProperties(TypeElement type, TypeElementMembers members,
|
||||
ExecutableElement constructor) {
|
||||
Map<String, PropertyDescriptor<?>> candidates = new LinkedHashMap<>();
|
||||
constructor.getParameters().forEach((parameter) -> {
|
||||
String name = getParameterName(parameter);
|
||||
TypeMirror propertyType = parameter.asType();
|
||||
ExecutableElement getter = members.getPublicGetter(name, propertyType);
|
||||
ExecutableElement setter = members.getPublicSetter(name, propertyType);
|
||||
VariableElement field = members.getFields().get(name);
|
||||
RecordComponentElement recordComponent = members.getRecordComponents().get(name);
|
||||
register(candidates, new ConstructorParameterPropertyDescriptor(type, null, parameter, name, propertyType,
|
||||
field, recordComponent, getter, setter));
|
||||
private Stream<PropertyDescriptor> resolveConstructorBoundProperties(TypeElement declaringElement,
|
||||
TypeElementMembers members, ExecutableElement bindConstructor) {
|
||||
Map<String, PropertyDescriptor> candidates = new LinkedHashMap<>();
|
||||
bindConstructor.getParameters().forEach((parameter) -> {
|
||||
PropertyDescriptor descriptor = extracted(declaringElement, members, parameter);
|
||||
register(candidates, descriptor);
|
||||
});
|
||||
return candidates.values().stream();
|
||||
}
|
||||
|
||||
private PropertyDescriptor extracted(TypeElement declaringElement, TypeElementMembers members,
|
||||
VariableElement parameter) {
|
||||
String name = getParameterName(parameter);
|
||||
TypeMirror type = parameter.asType();
|
||||
ExecutableElement getter = members.getPublicGetter(name, type);
|
||||
ExecutableElement setter = members.getPublicSetter(name, type);
|
||||
VariableElement field = members.getFields().get(name);
|
||||
RecordComponentElement recordComponent = members.getRecordComponents().get(name);
|
||||
return (recordComponent != null)
|
||||
? new RecordParameterPropertyDescriptor(name, type, parameter, declaringElement, getter,
|
||||
recordComponent)
|
||||
: new ConstructorParameterPropertyDescriptor(name, type, parameter, declaringElement, getter, setter,
|
||||
field);
|
||||
}
|
||||
|
||||
private String getParameterName(VariableElement parameter) {
|
||||
AnnotationMirror nameAnnotation = this.environment.getNameAnnotation(parameter);
|
||||
if (nameAnnotation != null) {
|
||||
@@ -99,24 +106,24 @@ class PropertyDescriptorResolver {
|
||||
return parameter.getSimpleName().toString();
|
||||
}
|
||||
|
||||
Stream<PropertyDescriptor<?>> resolveJavaBeanProperties(TypeElement type, ExecutableElement factoryMethod,
|
||||
TypeElementMembers members) {
|
||||
private Stream<PropertyDescriptor> resolveJavaBeanProperties(TypeElement declaringElement,
|
||||
TypeElementMembers members, ExecutableElement factoryMethod) {
|
||||
// First check if we have regular java bean properties there
|
||||
Map<String, PropertyDescriptor<?>> candidates = new LinkedHashMap<>();
|
||||
Map<String, PropertyDescriptor> candidates = new LinkedHashMap<>();
|
||||
members.getPublicGetters().forEach((name, getters) -> {
|
||||
VariableElement field = members.getFields().get(name);
|
||||
ExecutableElement getter = findMatchingGetter(members, getters, field);
|
||||
TypeMirror propertyType = getter.getReturnType();
|
||||
register(candidates, new JavaBeanPropertyDescriptor(type, factoryMethod, getter, name, propertyType, field,
|
||||
members.getPublicSetter(name, propertyType)));
|
||||
register(candidates, new JavaBeanPropertyDescriptor(name, propertyType, declaringElement, getter,
|
||||
members.getPublicSetter(name, propertyType), field, factoryMethod));
|
||||
});
|
||||
// Then check for Lombok ones
|
||||
members.getFields().forEach((name, field) -> {
|
||||
TypeMirror propertyType = field.asType();
|
||||
ExecutableElement getter = members.getPublicGetter(name, propertyType);
|
||||
ExecutableElement setter = members.getPublicSetter(name, propertyType);
|
||||
register(candidates,
|
||||
new LombokPropertyDescriptor(type, factoryMethod, field, name, propertyType, getter, setter));
|
||||
register(candidates, new LombokPropertyDescriptor(name, propertyType, declaringElement, getter, setter,
|
||||
field, factoryMethod));
|
||||
});
|
||||
return candidates.values().stream();
|
||||
}
|
||||
@@ -129,20 +136,20 @@ class PropertyDescriptorResolver {
|
||||
return candidates.get(0);
|
||||
}
|
||||
|
||||
private void register(Map<String, PropertyDescriptor<?>> candidates, PropertyDescriptor<?> descriptor) {
|
||||
private void register(Map<String, PropertyDescriptor> candidates, PropertyDescriptor descriptor) {
|
||||
if (!candidates.containsKey(descriptor.getName()) && isCandidate(descriptor)) {
|
||||
candidates.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCandidate(PropertyDescriptor<?> descriptor) {
|
||||
private boolean isCandidate(PropertyDescriptor descriptor) {
|
||||
return descriptor.isProperty(this.environment) || descriptor.isNested(this.environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around a {@link TypeElement} that could be bound.
|
||||
*/
|
||||
private static class ConfigurationPropertiesTypeElement {
|
||||
private static class Bindable {
|
||||
|
||||
private final TypeElement type;
|
||||
|
||||
@@ -150,8 +157,7 @@ class PropertyDescriptorResolver {
|
||||
|
||||
private final List<ExecutableElement> boundConstructors;
|
||||
|
||||
ConfigurationPropertiesTypeElement(TypeElement type, List<ExecutableElement> constructors,
|
||||
List<ExecutableElement> boundConstructors) {
|
||||
Bindable(TypeElement type, List<ExecutableElement> constructors, List<ExecutableElement> boundConstructors) {
|
||||
this.type = type;
|
||||
this.constructors = constructors;
|
||||
this.boundConstructors = boundConstructors;
|
||||
@@ -188,10 +194,10 @@ class PropertyDescriptorResolver {
|
||||
return boundConstructor;
|
||||
}
|
||||
|
||||
static ConfigurationPropertiesTypeElement of(TypeElement type, MetadataGenerationEnvironment env) {
|
||||
static Bindable of(TypeElement type, MetadataGenerationEnvironment env) {
|
||||
List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
|
||||
List<ExecutableElement> boundConstructors = getBoundConstructors(type, env, constructors);
|
||||
return new ConfigurationPropertiesTypeElement(type, constructors, boundConstructors);
|
||||
return new Bindable(type, constructors, boundConstructors);
|
||||
}
|
||||
|
||||
private static List<ExecutableElement> getBoundConstructors(TypeElement type, MetadataGenerationEnvironment env,
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-2024 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.boot.configurationprocessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.RecordComponentElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
/**
|
||||
* A {@link PropertyDescriptor} for a record parameter.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Pavel Anisimov
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class RecordParameterPropertyDescriptor extends ParameterPropertyDescriptor {
|
||||
|
||||
private final RecordComponentElement recordComponent;
|
||||
|
||||
RecordParameterPropertyDescriptor(String name, TypeMirror type, VariableElement parameter,
|
||||
TypeElement declaringElement, ExecutableElement getter, RecordComponentElement recordComponent) {
|
||||
super(name, type, parameter, declaringElement, getter);
|
||||
this.recordComponent = recordComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Element> getDeprecatableElements() {
|
||||
return Arrays.asList(getGetter());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMarkedAsNested(MetadataGenerationEnvironment environment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveDescription(MetadataGenerationEnvironment environment) {
|
||||
return environment.getTypeUtils().getJavaDoc(this.recordComponent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -46,7 +46,7 @@ class TypeElementMembers {
|
||||
|
||||
private static final String OBJECT_CLASS_NAME = Object.class.getName();
|
||||
|
||||
private static final String RECORD_CLASS_NAME = "java.lang.Record";
|
||||
private static final String RECORD_CLASS_NAME = Record.class.getName();
|
||||
|
||||
private final MetadataGenerationEnvironment env;
|
||||
|
||||
@@ -73,12 +73,12 @@ class TypeElementMembers {
|
||||
for (VariableElement field : ElementFilter.fieldsIn(element.getEnclosedElements())) {
|
||||
processField(field);
|
||||
}
|
||||
for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
|
||||
processMethod(method);
|
||||
}
|
||||
for (RecordComponentElement recordComponent : ElementFilter.recordComponentsIn(element.getEnclosedElements())) {
|
||||
processRecordComponent(recordComponent);
|
||||
}
|
||||
for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
|
||||
processMethod(method);
|
||||
}
|
||||
Element superType = this.env.getTypeUtils().asElement(element.getSuperclass());
|
||||
if (superType instanceof TypeElement && !OBJECT_CLASS_NAME.equals(superType.toString())
|
||||
&& !RECORD_CLASS_NAME.equals(superType.toString())) {
|
||||
@@ -198,9 +198,7 @@ class TypeElementMembers {
|
||||
|
||||
private void processRecordComponent(RecordComponentElement recordComponent) {
|
||||
String name = recordComponent.getSimpleName().toString();
|
||||
if (!this.recordComponents.containsKey(name)) {
|
||||
this.recordComponents.put(name, recordComponent);
|
||||
}
|
||||
this.recordComponents.putIfAbsent(name, recordComponent);
|
||||
}
|
||||
|
||||
Map<String, VariableElement> getFields() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -48,7 +48,7 @@ class ConstructorParameterPropertyDescriptorTests extends PropertyDescriptorTest
|
||||
TypeElement ownerElement = roundEnv.getRootElement(ImmutableSimpleProperties.class);
|
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor(ownerElement, "theName");
|
||||
assertThat(property.getName()).isEqualTo("theName");
|
||||
assertThat(property.getSource()).hasToString("theName");
|
||||
assertThat(property.getParameter()).hasToString("theName");
|
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getTheName");
|
||||
assertThat(property.isProperty(metadataEnv)).isTrue();
|
||||
assertThat(property.isNested(metadataEnv)).isFalse();
|
||||
@@ -61,7 +61,7 @@ class ConstructorParameterPropertyDescriptorTests extends PropertyDescriptorTest
|
||||
TypeElement ownerElement = roundEnv.getRootElement(ImmutableInnerClassProperties.class);
|
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor(ownerElement, "first");
|
||||
assertThat(property.getName()).isEqualTo("first");
|
||||
assertThat(property.getSource()).hasToString("first");
|
||||
assertThat(property.getParameter()).hasToString("first");
|
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getFirst");
|
||||
assertThat(property.isProperty(metadataEnv)).isFalse();
|
||||
assertThat(property.isNested(metadataEnv)).isTrue();
|
||||
@@ -74,7 +74,7 @@ class ConstructorParameterPropertyDescriptorTests extends PropertyDescriptorTest
|
||||
TypeElement ownerElement = roundEnv.getRootElement(ImmutableInnerClassProperties.class);
|
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor(ownerElement, "third");
|
||||
assertThat(property.getName()).isEqualTo("third");
|
||||
assertThat(property.getSource()).hasToString("third");
|
||||
assertThat(property.getParameter()).hasToString("third");
|
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getThird");
|
||||
assertThat(property.isProperty(metadataEnv)).isFalse();
|
||||
assertThat(property.isNested(metadataEnv)).isTrue();
|
||||
@@ -87,7 +87,7 @@ class ConstructorParameterPropertyDescriptorTests extends PropertyDescriptorTest
|
||||
TypeElement ownerElement = roundEnv.getRootElement(ImmutableSimpleProperties.class);
|
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor(ownerElement, "counter");
|
||||
assertThat(property.getName()).isEqualTo("counter");
|
||||
assertThat(property.getSource()).hasToString("counter");
|
||||
assertThat(property.getParameter()).hasToString("counter");
|
||||
assertThat(property.getGetter()).isNull();
|
||||
assertThat(property.isProperty(metadataEnv)).isTrue();
|
||||
assertThat(property.isNested(metadataEnv)).isFalse();
|
||||
@@ -130,8 +130,8 @@ class ConstructorParameterPropertyDescriptorTests extends PropertyDescriptorTest
|
||||
ExecutableElement getter = getMethod(ownerElement, "isFlag");
|
||||
VariableElement field = getField(ownerElement, "flag");
|
||||
VariableElement constructorParameter = getConstructorParameter(ownerElement, "flag");
|
||||
ConstructorParameterPropertyDescriptor property = new ConstructorParameterPropertyDescriptor(ownerElement,
|
||||
null, constructorParameter, "flag", field.asType(), field, null, getter, null);
|
||||
ConstructorParameterPropertyDescriptor property = new ConstructorParameterPropertyDescriptor("flag",
|
||||
field.asType(), constructorParameter, ownerElement, getter, null, field);
|
||||
assertItemMetadata(metadataEnv, property).isProperty().isDeprecatedWithNoInformation();
|
||||
});
|
||||
}
|
||||
@@ -222,8 +222,8 @@ class ConstructorParameterPropertyDescriptorTests extends PropertyDescriptorTest
|
||||
VariableElement field = getField(ownerElement, name);
|
||||
ExecutableElement getter = getMethod(ownerElement, createAccessorMethodName("get", name));
|
||||
ExecutableElement setter = getMethod(ownerElement, createAccessorMethodName("set", name));
|
||||
return new ConstructorParameterPropertyDescriptor(ownerElement, null, constructorParameter, name,
|
||||
field.asType(), field, null, getter, setter);
|
||||
return new ConstructorParameterPropertyDescriptor(name, field.asType(), constructorParameter, ownerElement,
|
||||
getter, setter, field);
|
||||
}
|
||||
|
||||
private VariableElement getConstructorParameter(TypeElement ownerElement, String name) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -43,7 +43,6 @@ class JavaBeanPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(SimpleTypeProperties.class);
|
||||
JavaBeanPropertyDescriptor property = createPropertyDescriptor(ownerElement, "myString");
|
||||
assertThat(property.getName()).isEqualTo("myString");
|
||||
assertThat(property.getSource()).isSameAs(property.getGetter());
|
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getMyString");
|
||||
assertThat(property.getSetter().getSimpleName()).hasToString("setMyString");
|
||||
assertThat(property.isProperty(metadataEnv)).isTrue();
|
||||
@@ -96,10 +95,9 @@ class JavaBeanPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(SimpleProperties.class);
|
||||
ExecutableElement getter = getMethod(ownerElement, "getSize");
|
||||
VariableElement field = getField(ownerElement, "size");
|
||||
JavaBeanPropertyDescriptor property = new JavaBeanPropertyDescriptor(ownerElement, getter, getter, "size",
|
||||
field.asType(), field, null);
|
||||
JavaBeanPropertyDescriptor property = new JavaBeanPropertyDescriptor("size", field.asType(), ownerElement,
|
||||
getter, null, field, getter);
|
||||
assertThat(property.getName()).isEqualTo("size");
|
||||
assertThat(property.getSource()).isSameAs(property.getGetter());
|
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getSize");
|
||||
assertThat(property.getSetter()).isNull();
|
||||
assertThat(property.isProperty(metadataEnv)).isFalse();
|
||||
@@ -112,10 +110,9 @@ class JavaBeanPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
process(SimpleProperties.class, (roundEnv, metadataEnv) -> {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(SimpleProperties.class);
|
||||
VariableElement field = getField(ownerElement, "counter");
|
||||
JavaBeanPropertyDescriptor property = new JavaBeanPropertyDescriptor(ownerElement, null, null, "counter",
|
||||
field.asType(), field, getMethod(ownerElement, "setCounter"));
|
||||
JavaBeanPropertyDescriptor property = new JavaBeanPropertyDescriptor("counter", field.asType(),
|
||||
ownerElement, null, getMethod(ownerElement, "setCounter"), field, null);
|
||||
assertThat(property.getName()).isEqualTo("counter");
|
||||
assertThat(property.getSource()).isSameAs(property.getGetter());
|
||||
assertThat(property.getGetter()).isNull();
|
||||
assertThat(property.getSetter().getSimpleName()).hasToString("setCounter");
|
||||
assertThat(property.isProperty(metadataEnv)).isFalse();
|
||||
@@ -171,8 +168,8 @@ class JavaBeanPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
process(SimpleProperties.class, (roundEnv, metadataEnv) -> {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(SimpleProperties.class);
|
||||
VariableElement field = getField(ownerElement, "counter");
|
||||
JavaBeanPropertyDescriptor property = new JavaBeanPropertyDescriptor(ownerElement, null, null, "counter",
|
||||
field.asType(), field, getMethod(ownerElement, "setCounter"));
|
||||
JavaBeanPropertyDescriptor property = new JavaBeanPropertyDescriptor("counter", field.asType(),
|
||||
ownerElement, null, getMethod(ownerElement, "setCounter"), field, null);
|
||||
assertThat(property.resolveItemMetadata("test", metadataEnv)).isNull();
|
||||
});
|
||||
}
|
||||
@@ -247,7 +244,7 @@ class JavaBeanPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
ExecutableElement getter = getMethod(ownerElement, getterName);
|
||||
ExecutableElement setter = getMethod(ownerElement, setterName);
|
||||
VariableElement field = getField(ownerElement, name);
|
||||
return new JavaBeanPropertyDescriptor(ownerElement, null, getter, name, getter.getReturnType(), field, setter);
|
||||
return new JavaBeanPropertyDescriptor(name, getter.getReturnType(), ownerElement, getter, setter, field, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -47,7 +47,6 @@ class LombokPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(LombokSimpleProperties.class);
|
||||
LombokPropertyDescriptor property = createPropertyDescriptor(ownerElement, "name");
|
||||
assertThat(property.getName()).isEqualTo("name");
|
||||
assertThat(property.getSource()).isSameAs(property.getField());
|
||||
assertThat(property.getField().getSimpleName()).hasToString("name");
|
||||
assertThat(property.isProperty(metadataEnv)).isTrue();
|
||||
assertThat(property.isNested(metadataEnv)).isFalse();
|
||||
@@ -60,7 +59,6 @@ class LombokPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(LombokSimpleProperties.class);
|
||||
LombokPropertyDescriptor property = createPropertyDescriptor(ownerElement, "items");
|
||||
assertThat(property.getName()).isEqualTo("items");
|
||||
assertThat(property.getSource()).isSameAs(property.getField());
|
||||
assertThat(property.getField().getSimpleName()).hasToString("items");
|
||||
assertThat(property.isProperty(metadataEnv)).isTrue();
|
||||
assertThat(property.isNested(metadataEnv)).isFalse();
|
||||
@@ -73,7 +71,6 @@ class LombokPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(LombokInnerClassProperties.class);
|
||||
LombokPropertyDescriptor property = createPropertyDescriptor(ownerElement, "first");
|
||||
assertThat(property.getName()).isEqualTo("first");
|
||||
assertThat(property.getSource()).isSameAs(property.getField());
|
||||
assertThat(property.getField().getSimpleName()).hasToString("first");
|
||||
assertThat(property.isProperty(metadataEnv)).isFalse();
|
||||
assertThat(property.isNested(metadataEnv)).isTrue();
|
||||
@@ -86,7 +83,6 @@ class LombokPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(LombokInnerClassProperties.class);
|
||||
LombokPropertyDescriptor property = createPropertyDescriptor(ownerElement, "third");
|
||||
assertThat(property.getName()).isEqualTo("third");
|
||||
assertThat(property.getSource()).isSameAs(property.getField());
|
||||
assertThat(property.getField().getSimpleName()).hasToString("third");
|
||||
assertThat(property.isProperty(metadataEnv)).isFalse();
|
||||
assertThat(property.isNested(metadataEnv)).isTrue();
|
||||
@@ -177,8 +173,8 @@ class LombokPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
TypeElement ownerElement = roundEnv.getRootElement(LombokInnerClassProperties.class);
|
||||
VariableElement field = getField(ownerElement, "third");
|
||||
ExecutableElement getter = getMethod(ownerElement, "getThird");
|
||||
LombokPropertyDescriptor property = new LombokPropertyDescriptor(ownerElement, null, field, "third",
|
||||
field.asType(), getter, null);
|
||||
LombokPropertyDescriptor property = new LombokPropertyDescriptor("third", field.asType(), ownerElement,
|
||||
getter, null, field, null);
|
||||
assertItemMetadata(metadataEnv, property).isGroup()
|
||||
.hasName("test.third")
|
||||
.hasType("org.springframework.boot.configurationsample.lombok.SimpleLombokPojo")
|
||||
@@ -276,7 +272,7 @@ class LombokPropertyDescriptorTests extends PropertyDescriptorTests {
|
||||
VariableElement field = getField(ownerElement, name);
|
||||
ExecutableElement getter = getMethod(ownerElement, createAccessorMethodName("get", name));
|
||||
ExecutableElement setter = getMethod(ownerElement, createAccessorMethodName("set", name));
|
||||
return new LombokPropertyDescriptor(ownerElement, null, field, name, field.asType(), getter, setter);
|
||||
return new LombokPropertyDescriptor(name, field.asType(), ownerElement, getter, setter, field, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -161,7 +161,7 @@ class PropertyDescriptorResolverTests {
|
||||
}
|
||||
|
||||
private BiConsumer<TypeElement, MetadataGenerationEnvironment> properties(
|
||||
Consumer<Stream<PropertyDescriptor<?>>> stream) {
|
||||
Consumer<Stream<PropertyDescriptor>> stream) {
|
||||
return (element, metadataEnv) -> {
|
||||
PropertyDescriptorResolver resolver = new PropertyDescriptorResolver(metadataEnv);
|
||||
stream.accept(resolver.resolve(element, null));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 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.
|
||||
@@ -61,7 +61,7 @@ public abstract class PropertyDescriptorTests {
|
||||
}
|
||||
|
||||
protected ItemMetadataAssert assertItemMetadata(MetadataGenerationEnvironment metadataEnv,
|
||||
PropertyDescriptor<?> property) {
|
||||
PropertyDescriptor property) {
|
||||
return new ItemMetadataAssert(property.resolveItemMetadata("test", metadataEnv));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user