Add support for value provider

Improve the "hints" section of the metadata so that each hint can provide
the reference to a value provider.

A value provider defines how a tool can discover the potential values of
a property based on the context. The provider is identifed by a name and
may have an arbitrary number of parameters.

Closes gh-3303
This commit is contained in:
Stephane Nicoll
2015-06-23 17:55:50 +02:00
parent 43b9ea53d6
commit 0ec9de9137
6 changed files with 511 additions and 12 deletions

View File

@@ -19,6 +19,8 @@ package org.springframework.boot.configurationprocessor;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -369,6 +371,20 @@ public class ConfigurationMetadataAnnotationProcessorTests {
containsHint("simple.the-name").withValue(0, "boot", "Bla bla"));
}
@Test
public void mergingOfHintWithProvider() throws Exception {
writeAdditionalHints(
new ItemHint("simple.theName", Collections.<ItemHint.ValueHint>emptyList(), Arrays.asList(
new ItemHint.ProviderHint("first", Collections.<String,Object>singletonMap("target", "org.foo")),
new ItemHint.ProviderHint("second", null))
));
ConfigurationMetadata metadata = compile(SimpleProperties.class);
assertThat(metadata, containsHint("simple.the-name")
.withProvider("first", "target", "org.foo")
.withProvider("second"));
}
@Test
public void incrementalBuild() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, FooProperties.class,

View File

@@ -17,11 +17,14 @@
package org.springframework.boot.configurationprocessor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.collection.IsMapContaining;
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
import org.springframework.boot.configurationprocessor.metadata.ItemHint;
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
@@ -206,13 +209,16 @@ public class ConfigurationMetadataMatchers {
private final List<ValueHintMatcher> values;
private final List<ProviderHintMatcher> providers;
public ContainsHintMatcher(String name) {
this(name, new ArrayList<ValueHintMatcher>());
this(name, new ArrayList<ValueHintMatcher>(), new ArrayList<ProviderHintMatcher>());
}
public ContainsHintMatcher(String name, List<ValueHintMatcher> values) {
public ContainsHintMatcher(String name, List<ValueHintMatcher> values, List<ProviderHintMatcher> providers) {
this.name = name;
this.values = values;
this.providers = providers;
}
@Override
@@ -230,6 +236,11 @@ public class ConfigurationMetadataMatchers {
return false;
}
}
for (ProviderHintMatcher provider : this.providers) {
if (!provider.matches(itemHint)) {
return false;
}
}
return true;
}
@@ -251,12 +262,29 @@ public class ConfigurationMetadataMatchers {
if (this.values != null) {
description.appendText(" values ").appendValue(this.values);
}
if (this.providers != null) {
description.appendText(" providers ").appendValue(this.providers);
}
}
public ContainsHintMatcher withValue(int index, Object value, String description) {
List<ValueHintMatcher> values = new ArrayList<ValueHintMatcher>(this.values);
values.add(new ValueHintMatcher(index, value, description));
return new ContainsHintMatcher(this.name, values);
return new ContainsHintMatcher(this.name, values, this.providers);
}
public ContainsHintMatcher withProvider(int index, String provider, Map<String,Object> parameters) {
List<ProviderHintMatcher> providers = new ArrayList<ProviderHintMatcher>(this.providers);
providers.add(new ProviderHintMatcher(index, provider, parameters));
return new ContainsHintMatcher(this.name, this.values, providers);
}
public ContainsHintMatcher withProvider(String provider, String key, Object value) {
return withProvider(this.providers.size(), provider, Collections.singletonMap(key, value));
}
public ContainsHintMatcher withProvider(String provider) {
return withProvider(this.providers.size(), provider, null);
}
private ItemHint getFirstHintWithName(ConfigurationMetadata metadata, String name) {
@@ -314,4 +342,50 @@ public class ConfigurationMetadataMatchers {
}
public static class ProviderHintMatcher extends BaseMatcher<ItemHint> {
private final int index;
private final String name;
private final Map<String, Object> parameters;
public ProviderHintMatcher(int index, String name, Map<String, Object> parameters) {
this.index = index;
this.name = name;
this.parameters = parameters;
}
@Override
public boolean matches(Object item) {
ItemHint hint = (ItemHint) item;
if (this.index + 1 > hint.getProviders().size()) {
return false;
}
ItemHint.ProviderHint providerHint = hint.getProviders().get(index);
if (this.name != null
&& !this.name.equals(providerHint.getName())) {
return false;
}
if (this.parameters != null) {
for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
if (!IsMapContaining.hasEntry(entry.getKey(), entry.getValue())
.matches(providerHint.getParameters())) {
return false;
}
}
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("provider hint ");
if (this.name != null) {
description.appendText(" name ").appendValue(this.name);
}
if (this.parameters != null) {
description.appendText(" parameters ").appendValue(this.parameters);
}
}
}
}

View File

@@ -20,6 +20,8 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Test;
@@ -56,6 +58,9 @@ public class JsonMarshallerTests {
metadata.add(ItemHint.newHint("a.b"));
metadata.add(ItemHint.newHint("c", new ItemHint.ValueHint(123, "hey"),
new ItemHint.ValueHint(456, null)));
metadata.add(new ItemHint("d", null, Arrays.asList(
new ItemHint.ProviderHint("first", Collections.<String,Object>singletonMap("target", "foo")),
new ItemHint.ProviderHint("second", null))));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JsonMarshaller marshaller = new JsonMarshaller();
marshaller.write(metadata, outputStream);
@@ -76,6 +81,9 @@ public class JsonMarshallerTests {
assertThat(read, containsHint("a.b"));
assertThat(read,
containsHint("c").withValue(0, 123, "hey").withValue(1, 456, null));
assertThat(read, containsHint("d")
.withProvider("first", "target", "foo")
.withProvider("second"));
}
}