Skip to content

Commit

Permalink
Add default value for arrays
Browse files Browse the repository at this point in the history
Previously, a property holding an array did not have a proper default
value in the meta-data even though the related field was initialized
properly.

An explicit support for arrays has been added. The "defaultValue" now
holds the default value for singular properties or an array of values for
array-based properties. If the value is initalized with an empty array,
the default value is an empty array as well.

Closes spring-projectsgh-1996
  • Loading branch information
snicoll committed Dec 3, 2014
1 parent 0b19884 commit 7d57cb7
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.springframework.boot.configurationprocessor.fieldvalues.javac;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
* Reflection based access to {@code com.sun.source.tree.ExpressionTree}.
Expand All @@ -30,6 +32,10 @@ class ExpressionTree extends ReflectionWrapper {

private final Method literalValueMethod = findMethod(this.literalTreeType, "getValue");

private final Class<?> newArrayTreeType = findClass("com.sun.source.tree.NewArrayTree");

private final Method arrayValueMethod = findMethod(this.newArrayTreeType, "getInitializers");

public ExpressionTree(Object instance) {
super(instance);
}
Expand All @@ -45,4 +51,19 @@ public Object getLiteralValue() throws Exception {
return null;
}

public List<? extends ExpressionTree> getArrayExpression() throws Exception {
if (this.newArrayTreeType.isAssignableFrom(getInstance().getClass())) {
List<?> elements = (List<?>) this.arrayValueMethod.invoke(getInstance());
List<ExpressionTree> result = new ArrayList<ExpressionTree>();
if (elements == null) {
return result;
}
for (Object element : elements) {
result.add(new ExpressionTree(element));
}
return result;
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -107,18 +108,33 @@ public void visitVariable(VariableTree variable) throws Exception {
private Object getValue(VariableTree variable) throws Exception {
ExpressionTree initializer = variable.getInitializer();
Class<?> wrapperType = WRAPPER_TYPES.get(variable.getType());
Object defaultValue = DEFAULT_TYPE_VALUES.get(wrapperType);
if (initializer != null) {
if (initializer.getLiteralValue() != null) {
return initializer.getLiteralValue();
}
if (initializer.getKind().equals("IDENTIFIER")) {
return this.staticFinals.get(initializer.toString());
}
if (initializer.getKind().equals("MEMBER_SELECT")) {
return WELL_KNOWN_STATIC_FINALS.get(initializer.toString());
return getValue(initializer, defaultValue);
}
return defaultValue;
}

private Object getValue(ExpressionTree expression, Object defaultValue) throws Exception {
Object literalValue = expression.getLiteralValue();
if (literalValue != null) {
return literalValue;
}
List<? extends ExpressionTree> arrayValues = expression.getArrayExpression();
if (arrayValues != null) {
Object[] result = new Object[arrayValues.size()];
for (int i = 0; i < arrayValues.size(); i++) {
result[i] = getValue(arrayValues.get(i), null);
}
return result;
}
if (expression.getKind().equals("IDENTIFIER")) {
return this.staticFinals.get(expression.toString());
}
if (expression.getKind().equals("MEMBER_SELECT")) {
return WELL_KNOWN_STATIC_FINALS.get(expression.toString());
}
return DEFAULT_TYPE_VALUES.get(wrapperType);
return defaultValue;
}

public Map<String, Object> getFieldValues() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.nio.charset.Charset;
import java.util.LinkedHashSet;
import java.util.Set;
Expand Down Expand Up @@ -67,7 +68,10 @@ private JSONObject toJsonObject(ItemMetadata item) {
putIfPresent(jsonObject, "description", item.getDescription());
putIfPresent(jsonObject, "sourceType", item.getSourceType());
putIfPresent(jsonObject, "sourceMethod", item.getSourceMethod());
putIfPresent(jsonObject, "defaultValue", item.getDefaultValue());
Object defaultValue = item.getDefaultValue();
if (defaultValue != null) {
putDefaultValue(jsonObject, defaultValue);
}
if (item.isDeprecated()) {
jsonObject.put("deprecated", true);
}
Expand All @@ -80,6 +84,20 @@ private void putIfPresent(JSONObject jsonObject, String name, Object value) {
}
}

private void putDefaultValue(JSONObject jsonObject, Object value) {
Object defaultValue = value;
if (value.getClass().isArray()) {
JSONArray array = new JSONArray();
int length = Array.getLength(value);
for (int i = 0; i < length; i++) {
array.put(Array.get(value, i));
}
defaultValue = array;

}
jsonObject.put("defaultValue", defaultValue);
}

public ConfigurationMetadata read(InputStream inputStream) throws IOException {
ConfigurationMetadata metadata = new ConfigurationMetadata();
JSONObject object = new JSONObject(toString(inputStream));
Expand All @@ -105,12 +123,25 @@ private ItemMetadata toItemMetadata(JSONObject object, ItemType itemType) {
String description = object.optString("description", null);
String sourceType = object.optString("sourceType", null);
String sourceMethod = object.optString("sourceMethod", null);
Object defaultValue = object.opt("defaultValue");
Object defaultValue = readDefaultValue(object);
boolean deprecated = object.optBoolean("deprecated");
return new ItemMetadata(itemType, name, null, type, sourceType, sourceMethod,
description, defaultValue, deprecated);
}

private Object readDefaultValue(JSONObject object) {
Object defaultValue = object.opt("defaultValue");
if (defaultValue instanceof JSONArray) {
JSONArray array = (JSONArray) defaultValue;
Object[] content = new Object[array.length()];
for (int i = 0; i < array.length(); i++) {
content[i] = array.get(i);
}
return content;
}
return defaultValue;
}

private String toString(InputStream inputStream) throws IOException {
StringBuilder out = new StringBuilder();
InputStreamReader reader = new InputStreamReader(inputStream, UTF_8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.springframework.boot.configurationsample.specific.InnerClassRootConfig;
import org.springframework.boot.configurationsample.specific.SimplePojo;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
Expand Down Expand Up @@ -77,7 +78,7 @@ public void simpleProperties() throws Exception {
containsProperty("simple.the-name", String.class)
.fromSource(SimpleProperties.class)
.withDescription("The name of this simple properties.")
.withDefaultValue("boot").withDeprecated());
.withDefaultValue(is("boot")).withDeprecated());
assertThat(
metadata,
containsProperty("simple.flag", Boolean.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public static class ContainsItemMatcher extends BaseMatcher<ConfigurationMetadat

private final String description;

private final Object defaultValue;
private final Matcher<?> defaultValue;

private final boolean deprecated;

Expand All @@ -75,7 +75,7 @@ public ContainsItemMatcher(ItemType itemType, String name) {
}

public ContainsItemMatcher(ItemType itemType, String name, String type,
Class<?> sourceType, String description, Object defaultValue,
Class<?> sourceType, String description, Matcher<?> defaultValue,
boolean deprecated) {
this.itemType = itemType;
this.name = name;
Expand All @@ -101,7 +101,7 @@ public boolean matches(Object item) {
return false;
}
if (this.defaultValue != null
&& !this.defaultValue.equals(itemMetadata.getDefaultValue())) {
&& !this.defaultValue.matches(itemMetadata.getDefaultValue())) {
return false;
}
if (this.description != null
Expand Down Expand Up @@ -169,7 +169,7 @@ public ContainsItemMatcher withDescription(String description) {
this.sourceType, description, this.defaultValue, this.deprecated);
}

public ContainsItemMatcher withDefaultValue(Object defaultValue) {
public ContainsItemMatcher withDefaultValue(Matcher<?> defaultValue) {
return new ContainsItemMatcher(this.itemType, this.name, this.type,
this.sourceType, this.description, defaultValue, this.deprecated);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* Abstract base class for {@link FieldValuesParser} tests.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
public abstract class AbstractFieldValuesProcessorTests {

Expand Down Expand Up @@ -77,6 +78,12 @@ public void getFieldValues() throws Exception {
assertThat(values.get("objectNone"), nullValue());
assertThat(values.get("objectConst"), equalToObject("c"));
assertThat(values.get("objectInstance"), nullValue());
assertThat(values.get("stringArray"), equalToObject(new Object[] {"FOO", "BAR"}));
assertThat(values.get("stringArrayNone"), nullValue());
assertThat(values.get("stringEmptyArray"), equalToObject(new Object[0]));
assertThat(values.get("stringArrayConst"), equalToObject(new Object[]{"OK", "KO"}));
assertThat(values.get("stringArrayConstElements"), equalToObject(new Object[] {"c"}));
assertThat(values.get("integerArray"), equalToObject(new Object[] {42, 24}));
}

private Matcher<Object> equalToObject(Object object) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

import org.junit.Test;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.array;
import static org.junit.Assert.assertThat;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsGroup;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsProperty;
Expand All @@ -31,6 +34,7 @@
* Tests for {@link JsonMarshaller}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
public class JsonMarshallerTests {

Expand All @@ -45,6 +49,10 @@ public void marshallAndUnmarshal() throws IOException {
false));
metadata.add(ItemMetadata.newProperty("d", null, null, null, null, null, true,
false));
metadata.add(ItemMetadata.newProperty("e", null, null, null, null, null, new String[]{"y", "n"},
false));
metadata.add(ItemMetadata.newProperty("f", null, null, null, null, null, new Boolean[]{true, false},
false));
metadata.add(ItemMetadata.newGroup("d", null, null, null));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JsonMarshaller marshaller = new JsonMarshaller();
Expand All @@ -53,10 +61,12 @@ public void marshallAndUnmarshal() throws IOException {
outputStream.toByteArray()));
assertThat(read,
containsProperty("a.b", StringBuffer.class).fromSource(InputStream.class)
.withDescription("desc").withDefaultValue("x").withDeprecated());
.withDescription("desc").withDefaultValue(is("x")).withDeprecated());
assertThat(read, containsProperty("b.c.d"));
assertThat(read, containsProperty("c").withDefaultValue(123));
assertThat(read, containsProperty("d").withDefaultValue(true));
assertThat(read, containsProperty("c").withDefaultValue(is(123)));
assertThat(read, containsProperty("d").withDefaultValue(is(true)));
assertThat(read, containsProperty("e").withDefaultValue(is(array(equalTo("y"), equalTo("n")))));
assertThat(read, containsProperty("f").withDefaultValue(is(array(equalTo(true), equalTo(false)))));
assertThat(read, containsGroup("d"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* Sample object containing fields with initial values.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
@SuppressWarnings("unused")
@ConfigurationProperties
Expand All @@ -37,6 +38,8 @@ public class FieldValues {

private static final Integer INTEGER_OBJ_CONST = 4;

private static final String[] STRING_ARRAY_CONST = new String[] {"OK", "KO"};

private String string = "1";

private String stringNone;
Expand Down Expand Up @@ -75,4 +78,16 @@ public class FieldValues {

private Object objectInstance = new StringBuffer();

private String[] stringArray = new String[] {"FOO", "BAR"};

private String[] stringArrayNone;

private String[] stringEmptyArray = new String[0];

private String[] stringArrayConst = STRING_ARRAY_CONST;

private String[] stringArrayConstElements = new String[] { STRING_CONST };

private Integer[] integerArray = new Integer[] {42, 24};

}

0 comments on commit 7d57cb7

Please sign in to comment.