27 changed files with 1422 additions and 24 deletions
@ -0,0 +1,224 @@
@@ -0,0 +1,224 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.stream.Collectors; |
||||
|
||||
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; |
||||
|
||||
/** |
||||
* A {@link PropertyDescriptor} for a constructor parameter. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
class ConstructorParameterPropertyDescriptor extends PropertyDescriptor<VariableElement> { |
||||
|
||||
ConstructorParameterPropertyDescriptor(TypeElement ownerElement, |
||||
ExecutableElement factoryMethod, VariableElement source, String name, |
||||
TypeMirror type, VariableElement field, ExecutableElement getter, |
||||
ExecutableElement setter) { |
||||
super(ownerElement, factoryMethod, source, name, type, field, getter, setter); |
||||
} |
||||
|
||||
@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); |
||||
} |
||||
|
||||
@Override |
||||
protected Object resolveDefaultValue(MetadataGenerationEnvironment environment) { |
||||
Object defaultValue = getDefaultValueFromAnnotation(environment, getSource()); |
||||
if (defaultValue != null) { |
||||
return defaultValue; |
||||
} |
||||
return getSource().asType().accept(DefaultPrimitiveTypeVisitor.INSTANCE, null); |
||||
} |
||||
|
||||
private Object getDefaultValueFromAnnotation( |
||||
MetadataGenerationEnvironment environment, Element element) { |
||||
AnnotationMirror defaultValueAnnotation = environment |
||||
.getDefaultValueAnnotation(element); |
||||
if (defaultValueAnnotation != null) { |
||||
List<String> defaultValue = (List<String>) environment |
||||
.getAnnotationElementValues(defaultValueAnnotation).get("value"); |
||||
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)) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
catch (IllegalArgumentException ex) { |
||||
environment.getMessager().printMessage(Kind.ERROR, ex.getMessage(), |
||||
element, defaultValueAnnotation); |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
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 class DefaultValueCoercionTypeVisitor |
||||
extends TypeKindVisitor8<Object, String> { |
||||
|
||||
private static final DefaultValueCoercionTypeVisitor INSTANCE = new DefaultValueCoercionTypeVisitor(); |
||||
|
||||
private Integer parseInteger(String value) { |
||||
try { |
||||
return Integer.valueOf(value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Invalid number representation '%s'", value)); |
||||
} |
||||
} |
||||
|
||||
private Double parseFloatingPoint(String value) { |
||||
try { |
||||
return Double.valueOf(value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
throw new IllegalArgumentException(String |
||||
.format("Invalid floating point representation '%s'", value)); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsBoolean(PrimitiveType t, String value) { |
||||
return Boolean.parseBoolean(value); |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsByte(PrimitiveType t, String value) { |
||||
return parseInteger(value); |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsShort(PrimitiveType t, String value) { |
||||
return parseInteger(value); |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsInt(PrimitiveType t, String value) { |
||||
return parseInteger(value); |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsLong(PrimitiveType t, String value) { |
||||
return parseInteger(value); |
||||
} |
||||
|
||||
@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 parseFloatingPoint(value); |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsDouble(PrimitiveType t, String value) { |
||||
return parseFloatingPoint(value); |
||||
} |
||||
|
||||
} |
||||
|
||||
private static 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 0; |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsShort(PrimitiveType t, Void ignore) { |
||||
return 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 0; |
||||
} |
||||
|
||||
@Override |
||||
public Object visitPrimitiveAsDouble(PrimitiveType t, Void ignore) { |
||||
return 0D; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,318 @@
@@ -0,0 +1,318 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.io.IOException; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import javax.lang.model.element.ExecutableElement; |
||||
import javax.lang.model.element.TypeElement; |
||||
import javax.lang.model.element.VariableElement; |
||||
import javax.lang.model.util.ElementFilter; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.boot.configurationsample.immutable.ImmutableCollectionProperties; |
||||
import org.springframework.boot.configurationsample.immutable.ImmutableInnerClassProperties; |
||||
import org.springframework.boot.configurationsample.immutable.ImmutablePrimitiveProperties; |
||||
import org.springframework.boot.configurationsample.immutable.ImmutablePrimitiveWithDefaultsProperties; |
||||
import org.springframework.boot.configurationsample.immutable.ImmutablePrimitiveWrapperWithDefaultsProperties; |
||||
import org.springframework.boot.configurationsample.immutable.ImmutableSimpleProperties; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link ConstructorParameterPropertyDescriptor}. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
public class ConstructorParameterPropertyDescriptorTests extends PropertyDescriptorTests { |
||||
|
||||
@Test |
||||
public void constructorParameterSimpleProperty() throws IOException { |
||||
process(ImmutableSimpleProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableSimpleProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "theName"); |
||||
assertThat(property.getName()).isEqualTo("theName"); |
||||
assertThat(property.getSource()).hasToString("theName"); |
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getTheName"); |
||||
assertThat(property.isProperty(metadataEnv)).isTrue(); |
||||
assertThat(property.isNested(metadataEnv)).isFalse(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterNestedPropertySameClass() throws IOException { |
||||
process(ImmutableInnerClassProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableInnerClassProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "first"); |
||||
assertThat(property.getName()).isEqualTo("first"); |
||||
assertThat(property.getSource()).hasToString("first"); |
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getFirst"); |
||||
assertThat(property.isProperty(metadataEnv)).isFalse(); |
||||
assertThat(property.isNested(metadataEnv)).isTrue(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterNestedPropertyWithAnnotation() throws IOException { |
||||
process(ImmutableInnerClassProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableInnerClassProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "third"); |
||||
assertThat(property.getName()).isEqualTo("third"); |
||||
assertThat(property.getSource()).hasToString("third"); |
||||
assertThat(property.getGetter().getSimpleName()).hasToString("getThird"); |
||||
assertThat(property.isProperty(metadataEnv)).isFalse(); |
||||
assertThat(property.isNested(metadataEnv)).isTrue(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterSimplePropertyWithNoAccessorShouldBeExposed() |
||||
throws IOException { |
||||
process(ImmutableSimpleProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableSimpleProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "counter"); |
||||
assertThat(property.getName()).isEqualTo("counter"); |
||||
assertThat(property.getSource()).hasToString("counter"); |
||||
assertThat(property.getGetter()).isNull(); |
||||
assertThat(property.isProperty(metadataEnv)).isTrue(); |
||||
assertThat(property.isNested(metadataEnv)).isFalse(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterMetadataSimpleProperty() throws IOException { |
||||
process(ImmutableSimpleProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableSimpleProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "counter"); |
||||
assertItemMetadata(metadataEnv, property).isProperty().hasName("test.counter") |
||||
.hasType(Long.class).hasSourceType(ImmutableSimpleProperties.class) |
||||
.hasNoDescription().isNotDeprecated(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterMetadataNestedGroup() throws IOException { |
||||
process(ImmutableInnerClassProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableInnerClassProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "first"); |
||||
assertItemMetadata(metadataEnv, property).isGroup().hasName("test.first") |
||||
.hasType( |
||||
"org.springframework.boot.configurationsample.immutable.ImmutableInnerClassProperties$Foo") |
||||
.hasSourceType(ImmutableInnerClassProperties.class) |
||||
.hasSourceMethod("getFirst()").hasNoDescription().isNotDeprecated(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterDeprecatedPropertyOnGetter() throws IOException { |
||||
process(ImmutableSimpleProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableSimpleProperties.class); |
||||
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, getter, null); |
||||
assertItemMetadata(metadataEnv, property).isProperty() |
||||
.isDeprecatedWithNoInformation(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterPropertyWithDescription() throws IOException { |
||||
process(ImmutableSimpleProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableSimpleProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "theName"); |
||||
assertItemMetadata(metadataEnv, property).isProperty() |
||||
.hasDescription("The name of this simple properties."); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterPropertyWithDefaultValue() throws IOException { |
||||
process(ImmutableSimpleProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableSimpleProperties.class); |
||||
ConstructorParameterPropertyDescriptor property = createPropertyDescriptor( |
||||
ownerElement, "theName"); |
||||
assertItemMetadata(metadataEnv, property).isProperty() |
||||
.hasDefaultValue("boot"); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterPropertyWithPrimitiveTypes() throws IOException { |
||||
process(ImmutablePrimitiveProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutablePrimitiveProperties.class); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "flag")) |
||||
.hasDefaultValue(false); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "octet")).hasDefaultValue(0); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "letter")) |
||||
.hasDefaultValue(null); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "number")).hasDefaultValue(0); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "counter")).hasDefaultValue(0); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "value")).hasDefaultValue(0L); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "percentage")) |
||||
.hasDefaultValue(0); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "ratio")).hasDefaultValue(0D); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterPropertyWithPrimitiveTypesAndDefaultValues() |
||||
throws IOException { |
||||
process(ImmutablePrimitiveWithDefaultsProperties.class, |
||||
(roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv.getRootElement( |
||||
ImmutablePrimitiveWithDefaultsProperties.class); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "flag")) |
||||
.hasDefaultValue(true); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "octet")) |
||||
.hasDefaultValue(120); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "letter")) |
||||
.hasDefaultValue("a"); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "number")) |
||||
.hasDefaultValue(1000); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "counter")) |
||||
.hasDefaultValue(42); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "value")) |
||||
.hasDefaultValue(2000); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "percentage")) |
||||
.hasDefaultValue(0.5); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "ratio")) |
||||
.hasDefaultValue(42.42); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterPropertyWithPrimitiveWrapperTypesAndDefaultValues() |
||||
throws IOException { |
||||
process(ImmutablePrimitiveWrapperWithDefaultsProperties.class, |
||||
(roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv.getRootElement( |
||||
ImmutablePrimitiveWrapperWithDefaultsProperties.class); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "flag")) |
||||
.hasDefaultValue(true); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "octet")) |
||||
.hasDefaultValue(120); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "letter")) |
||||
.hasDefaultValue("a"); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "number")) |
||||
.hasDefaultValue(1000); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "counter")) |
||||
.hasDefaultValue(42); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "value")) |
||||
.hasDefaultValue(2000); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "percentage")) |
||||
.hasDefaultValue(0.5); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "ratio")) |
||||
.hasDefaultValue(42.42); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorParameterPropertyWithCollectionTypesAndDefaultValues() |
||||
throws IOException { |
||||
process(ImmutableCollectionProperties.class, (roundEnv, metadataEnv) -> { |
||||
TypeElement ownerElement = roundEnv |
||||
.getRootElement(ImmutableCollectionProperties.class); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "names")) |
||||
.hasDefaultValue(null); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "flags")) |
||||
.hasDefaultValue(Arrays.asList(true, false)); |
||||
assertItemMetadata(metadataEnv, |
||||
createPropertyDescriptor(ownerElement, "durations")) |
||||
.hasDefaultValue(Arrays.asList("10s", "1m", "1h")); |
||||
}); |
||||
} |
||||
|
||||
protected ConstructorParameterPropertyDescriptor createPropertyDescriptor( |
||||
TypeElement ownerElement, String name) { |
||||
VariableElement constructorParameter = getConstructorParameter(ownerElement, |
||||
name); |
||||
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, getter, setter); |
||||
} |
||||
|
||||
private VariableElement getConstructorParameter(TypeElement ownerElement, |
||||
String name) { |
||||
List<ExecutableElement> constructors = ElementFilter |
||||
.constructorsIn(ownerElement.getEnclosedElements()).stream() |
||||
.filter((constructor) -> !constructor.getParameters().isEmpty()) |
||||
.collect(Collectors.toList()); |
||||
if (constructors.size() != 1) { |
||||
throw new IllegalStateException( |
||||
"No candidate constructor for " + ownerElement); |
||||
} |
||||
return constructors.get(0).getParameters().stream() |
||||
.filter((parameter) -> parameter.getSimpleName().toString().equals(name)) |
||||
.findFirst().orElse(null); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* Copyright 2012-2019 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 org.junit.Test; |
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; |
||||
import org.springframework.boot.configurationprocessor.metadata.Metadata; |
||||
import org.springframework.boot.configurationsample.immutable.ImmutableSimpleProperties; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Metadata generation tests for immutable properties. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
public class ImmutablePropertiesMetadataGenerationTests |
||||
extends AbstractMetadataGenerationTests { |
||||
|
||||
@Test |
||||
public void immutableSimpleProperties() { |
||||
ConfigurationMetadata metadata = compile(ImmutableSimpleProperties.class); |
||||
assertThat(metadata).has(Metadata.withGroup("immutable") |
||||
.fromSource(ImmutableSimpleProperties.class)); |
||||
assertThat(metadata).has(Metadata.withProperty("immutable.the-name", String.class) |
||||
.fromSource(ImmutableSimpleProperties.class) |
||||
.withDescription("The name of this simple properties.") |
||||
.withDefaultValue("boot")); |
||||
assertThat(metadata).has(Metadata.withProperty("immutable.flag", Boolean.class) |
||||
.withDefaultValue(false).fromSource(ImmutableSimpleProperties.class) |
||||
.withDescription("A simple flag.").withDeprecation(null, null)); |
||||
assertThat(metadata).has(Metadata.withProperty("immutable.comparator")); |
||||
assertThat(metadata).has(Metadata.withProperty("immutable.counter")); |
||||
assertThat(metadata.getItems()).hasSize(5); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* Alternative to Spring Boot's {@code @DefaultValue} for testing (removes the need for a |
||||
* dependency on the real annotation). |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@Target({ ElementType.PARAMETER }) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface DefaultValue { |
||||
|
||||
String[] value(); |
||||
|
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.immutable; |
||||
|
||||
import java.time.Duration; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.boot.configurationsample.DefaultValue; |
||||
|
||||
/** |
||||
* Simple immutable properties with collections types and defaults. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
public class ImmutableCollectionProperties { |
||||
|
||||
private final List<String> names; |
||||
|
||||
private final List<Boolean> flags; |
||||
|
||||
private final List<Duration> durations; |
||||
|
||||
public ImmutableCollectionProperties(List<String> names, |
||||
@DefaultValue({ "true", "false" }) List<Boolean> flags, |
||||
@DefaultValue({ "10s", "1m", "1h" }) List<Duration> durations) { |
||||
this.names = names; |
||||
this.flags = flags; |
||||
this.durations = durations; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.immutable; |
||||
|
||||
import org.springframework.boot.configurationsample.NestedConfigurationProperty; |
||||
import org.springframework.boot.configurationsample.specific.SimplePojo; |
||||
|
||||
/** |
||||
* Inner properties, in immutable format. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
public class ImmutableInnerClassProperties { |
||||
|
||||
private final Foo first; |
||||
|
||||
private Foo second; |
||||
|
||||
@NestedConfigurationProperty |
||||
private final SimplePojo third; |
||||
|
||||
private final Fourth fourth; |
||||
|
||||
public ImmutableInnerClassProperties(Foo first, Foo second, SimplePojo third, |
||||
Fourth fourth) { |
||||
this.first = first; |
||||
this.second = second; |
||||
this.third = third; |
||||
this.fourth = fourth; |
||||
} |
||||
|
||||
public Foo getFirst() { |
||||
return this.first; |
||||
} |
||||
|
||||
public Foo getTheSecond() { |
||||
return this.second; |
||||
} |
||||
|
||||
public void setTheSecond(Foo second) { |
||||
this.second = second; |
||||
} |
||||
|
||||
public SimplePojo getThird() { |
||||
return this.third; |
||||
} |
||||
|
||||
public Fourth getFourth() { |
||||
return this.fourth; |
||||
} |
||||
|
||||
public static class Foo { |
||||
|
||||
private String name; |
||||
|
||||
private final Bar bar = new Bar(); |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public Bar getBar() { |
||||
return this.bar; |
||||
} |
||||
|
||||
public static class Bar { |
||||
|
||||
private String name; |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
public enum Fourth { |
||||
|
||||
YES, NO |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.immutable; |
||||
|
||||
/** |
||||
* Simple immutable properties with primitive types. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
public class ImmutablePrimitiveProperties { |
||||
|
||||
private final boolean flag; |
||||
|
||||
private final byte octet; |
||||
|
||||
private final char letter; |
||||
|
||||
private final short number; |
||||
|
||||
private final int counter; |
||||
|
||||
private final long value; |
||||
|
||||
private final float percentage; |
||||
|
||||
private final double ratio; |
||||
|
||||
public ImmutablePrimitiveProperties(boolean flag, byte octet, char letter, |
||||
short number, int counter, long value, float percentage, double ratio) { |
||||
this.flag = flag; |
||||
this.octet = octet; |
||||
this.letter = letter; |
||||
this.number = number; |
||||
this.counter = counter; |
||||
this.value = value; |
||||
this.percentage = percentage; |
||||
this.ratio = ratio; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.immutable; |
||||
|
||||
import org.springframework.boot.configurationsample.DefaultValue; |
||||
|
||||
/** |
||||
* Simple immutable properties with primitive types and defaults. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
public class ImmutablePrimitiveWithDefaultsProperties { |
||||
|
||||
private final boolean flag; |
||||
|
||||
private final byte octet; |
||||
|
||||
private final char letter; |
||||
|
||||
private final short number; |
||||
|
||||
private final int counter; |
||||
|
||||
private final long value; |
||||
|
||||
private final float percentage; |
||||
|
||||
private final double ratio; |
||||
|
||||
public ImmutablePrimitiveWithDefaultsProperties(@DefaultValue("true") boolean flag, |
||||
@DefaultValue("120") byte octet, @DefaultValue("a") char letter, |
||||
@DefaultValue("1000") short number, @DefaultValue("42") int counter, |
||||
@DefaultValue("2000") long value, @DefaultValue("0.5") float percentage, |
||||
@DefaultValue("42.42") double ratio) { |
||||
this.flag = flag; |
||||
this.octet = octet; |
||||
this.letter = letter; |
||||
this.number = number; |
||||
this.counter = counter; |
||||
this.value = value; |
||||
this.percentage = percentage; |
||||
this.ratio = ratio; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.immutable; |
||||
|
||||
import org.springframework.boot.configurationsample.DefaultValue; |
||||
|
||||
/** |
||||
* Simple immutable properties with primitive wrapper types and defaults. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
public class ImmutablePrimitiveWrapperWithDefaultsProperties { |
||||
|
||||
private final Boolean flag; |
||||
|
||||
private final Byte octet; |
||||
|
||||
private final Character letter; |
||||
|
||||
private final Short number; |
||||
|
||||
private final Integer counter; |
||||
|
||||
private final Long value; |
||||
|
||||
private final Float percentage; |
||||
|
||||
private final Double ratio; |
||||
|
||||
public ImmutablePrimitiveWrapperWithDefaultsProperties( |
||||
@DefaultValue("true") Boolean flag, @DefaultValue("120") Byte octet, |
||||
@DefaultValue("a") Character letter, @DefaultValue("1000") Short number, |
||||
@DefaultValue("42") Integer counter, @DefaultValue("2000") Long value, |
||||
@DefaultValue("0.5") Float percentage, @DefaultValue("42.42") Double ratio) { |
||||
this.flag = flag; |
||||
this.octet = octet; |
||||
this.letter = letter; |
||||
this.number = number; |
||||
this.counter = counter; |
||||
this.value = value; |
||||
this.percentage = percentage; |
||||
this.ratio = ratio; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.immutable; |
||||
|
||||
import java.util.Comparator; |
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties; |
||||
import org.springframework.boot.configurationsample.DefaultValue; |
||||
|
||||
/** |
||||
* Simple properties, in immutable format. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@ConfigurationProperties("immutable") |
||||
public class ImmutableSimpleProperties { |
||||
|
||||
/** |
||||
* The name of this simple properties. |
||||
*/ |
||||
private final String theName; |
||||
|
||||
/** |
||||
* A simple flag. |
||||
*/ |
||||
private final boolean flag; |
||||
|
||||
// An interface can still be injected because it might have a converter
|
||||
private final Comparator<?> comparator; |
||||
|
||||
// Even if it is not exposed, we're still offering a way to bind the value via the
|
||||
// constructor so it should be present in the metadata
|
||||
@SuppressWarnings("unused") |
||||
private final Long counter; |
||||
|
||||
public ImmutableSimpleProperties(@DefaultValue("boot") String theName, boolean flag, |
||||
Comparator<?> comparator, Long counter) { |
||||
this.theName = theName; |
||||
this.flag = flag; |
||||
this.comparator = comparator; |
||||
this.counter = counter; |
||||
} |
||||
|
||||
public String getTheName() { |
||||
return this.theName; |
||||
} |
||||
|
||||
@Deprecated |
||||
public boolean isFlag() { |
||||
return this.flag; |
||||
} |
||||
|
||||
public Comparator<?> getComparator() { |
||||
return this.comparator; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.specific; |
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties; |
||||
import org.springframework.boot.configurationsample.DefaultValue; |
||||
|
||||
/** |
||||
* Demonstrates that an invalid default character value leads to a compilation failure. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@ConfigurationProperties("test") |
||||
public class InvalidDefaultValueCharacterProperties { |
||||
|
||||
private final char letter; |
||||
|
||||
public InvalidDefaultValueCharacterProperties(@DefaultValue("bad") char letter) { |
||||
this.letter = letter; |
||||
} |
||||
|
||||
public char getLetter() { |
||||
return this.letter; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.specific; |
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties; |
||||
import org.springframework.boot.configurationsample.DefaultValue; |
||||
|
||||
/** |
||||
* Demonstrates that an invalid default floating point value leads to a compilation |
||||
* failure. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@ConfigurationProperties("test") |
||||
public class InvalidDefaultValueFloatingPointProperties { |
||||
|
||||
private final Double ratio; |
||||
|
||||
public InvalidDefaultValueFloatingPointProperties( |
||||
@DefaultValue("55.55.33") Double ratio) { |
||||
this.ratio = ratio; |
||||
} |
||||
|
||||
public Double getRatio() { |
||||
return this.ratio; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.specific; |
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties; |
||||
import org.springframework.boot.configurationsample.DefaultValue; |
||||
|
||||
/** |
||||
* Demonstrates that an invalid default number value leads to a compilation failure. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
@ConfigurationProperties("test") |
||||
public class InvalidDefaultValueNumberProperties { |
||||
|
||||
private final int counter; |
||||
|
||||
public InvalidDefaultValueNumberProperties(@DefaultValue("invalid") int counter) { |
||||
this.counter = counter; |
||||
} |
||||
|
||||
public int getCounter() { |
||||
return this.counter; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
/* |
||||
* Copyright 2012-2019 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.configurationsample.specific; |
||||
|
||||
/** |
||||
* A type with more than one constructor. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
public class TwoConstructorsExample { |
||||
|
||||
private String name; |
||||
|
||||
public TwoConstructorsExample() { |
||||
} |
||||
|
||||
public TwoConstructorsExample(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue