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:
parent
43b9ea53d6
commit
0ec9de9137
|
@ -71,6 +71,11 @@ categorized under "hints":
|
|||
"value": "force",
|
||||
"description": "Enable compression of all responses."
|
||||
},
|
||||
],
|
||||
"providers": [
|
||||
{
|
||||
"name": "any"
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
|
@ -210,6 +215,12 @@ The JSON object contained in the `hints` array can contain the following attribu
|
|||
| ValueHint[]
|
||||
| A list of valid values as defined by the `ValueHint` object (see below). Each entry defines
|
||||
the value and may have a description
|
||||
|
||||
|`providers`
|
||||
| ProviderHint[]
|
||||
| A list of providers as defined by the `ValueHint` object (see below). Each entry defines
|
||||
the name of the provider and its parameters, if any.
|
||||
|
||||
|===
|
||||
|
||||
The JSON object contained in the `values` array of each `hint` element can contain the
|
||||
|
@ -232,6 +243,24 @@ following attributes:
|
|||
end with a period (`.`).
|
||||
|===
|
||||
|
||||
The JSON object contained in the `providers` array of each `hint` element can contain the
|
||||
following attributes:
|
||||
|
||||
[cols="1,1,4"]
|
||||
|===
|
||||
|Name | Type |Purpose
|
||||
|
||||
|`name`
|
||||
| String
|
||||
| The name of the provider to use to offer additional content assistance for the element
|
||||
to which the hint refers to.
|
||||
|
||||
|`parameters`
|
||||
| JSON object
|
||||
| Any additional parameter that the provider supports (check the documentation of the
|
||||
provider for more details).
|
||||
|===
|
||||
|
||||
[[configuration-metadata-repeated-items]]
|
||||
==== Repeated meta-data items
|
||||
It is perfectly acceptable for "`property`" and "`group`" objects with the same name to
|
||||
|
@ -245,8 +274,14 @@ that they support such scenarios.
|
|||
[[configuration-metadata-providing-manual-hints]]
|
||||
=== Providing manual hints
|
||||
To improve the user experience and further assist the user in configuring a given
|
||||
property, you can provide additional meta-data that describes the list of potential
|
||||
values for a property.
|
||||
property, you can provide additional meta-data that:
|
||||
|
||||
1. Describes the list of potential values for a property.
|
||||
2. Associates a provider to attach a well-defined semantic to a property so that a tool
|
||||
can discover the list of potential values based on the project's context.
|
||||
|
||||
|
||||
==== Value hints
|
||||
|
||||
The `name` attribute of each hint refers to the `name` of a property. In the initial
|
||||
example above, we provide 3 values for the `server.tomcat.compression` property: `on`,
|
||||
|
@ -256,7 +291,302 @@ If your property is of type `Map`, you can provide hints for both the keys and t
|
|||
values (but not for the map itself). The special `.keys` and `.values` suffixes must
|
||||
be used to refer to the keys and the values respectively.
|
||||
|
||||
Let's assume a `foo.contexts` that maps magic String values to an integer:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@ConfigurationProperties("foo")
|
||||
public class FooProperties {
|
||||
|
||||
private Map<String,Integer> contexts;
|
||||
// getters and setters
|
||||
}
|
||||
----
|
||||
|
||||
The magic values are foo and bar for instance. In order to offer additional content
|
||||
assistance for the keys, you could add the following to
|
||||
<<configuration-metadata-additional-metadata,the manual meta-data of the module>>:
|
||||
|
||||
[source,json,indent=0]
|
||||
----
|
||||
{"hints": [
|
||||
{
|
||||
"name": "foo.contexts.keys",
|
||||
"values": [
|
||||
{
|
||||
"value": "foo"
|
||||
},
|
||||
{
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
----
|
||||
|
||||
NOTE: Of course, you should have an `Enum` for those two values instead. This is by far
|
||||
the most effective approach to auto-completion if your IDE supports it.
|
||||
|
||||
==== Provider hints
|
||||
|
||||
Providers are a powerful way of attaching semantics to a property. We define in the section
|
||||
below the official providers that you can use for your own hints. Bare in mind however that
|
||||
your favorite IDE may implement some of these or none of them. It could eventually provide
|
||||
its own as well.
|
||||
|
||||
NOTE: As this is a new feature, IDE vendors will have to catch up with this new feature.
|
||||
|
||||
The table below summarizes the list of supported providers:
|
||||
|
||||
[cols="2,4"]
|
||||
|===
|
||||
|Name | Description
|
||||
|
||||
|`any`
|
||||
|Permit any additional values to be provided.
|
||||
|
||||
|`class-reference`
|
||||
|Auto-complete the classes available in the project. Usually constrained by a base
|
||||
class that is specified via the `target` parameter.
|
||||
|
||||
|`enum`
|
||||
|Auto-complete the values of an enum given by the mandatory `target` parameter.
|
||||
|
||||
|`logger-name`
|
||||
|Auto-complete valid logger names. Typically, package and class names available in
|
||||
the current project can be auto-completed.
|
||||
|
||||
|`spring-bean-reference`
|
||||
|Auto-complete the available bean names in the current project. Usually constrained
|
||||
by a base class that is specified via the `target` parameter.
|
||||
|===
|
||||
|
||||
|
||||
TIP: No more than one provider can be active for a given property but you can specify
|
||||
several providers if they can all manage the property _in some ways_. Make sure to place
|
||||
the most powerful provider first as the IDE must use the first one in the JSON section it
|
||||
can handle. If no provider for a given property is supported, no special content
|
||||
assistance is provided either.
|
||||
|
||||
|
||||
===== Any
|
||||
|
||||
The **any** provider permits any additional values to be provided. Regular value
|
||||
validation based on the property type should be applied if this is supported.
|
||||
|
||||
This provider will be typically used if you have a list of values and any extra values
|
||||
are still to be considered as valid.
|
||||
|
||||
The example below offers `on` and `off` as auto-completion values for `system.state`; any
|
||||
other value is also allowed:
|
||||
|
||||
[source,json,indent=0]
|
||||
----
|
||||
{"hints": [
|
||||
{
|
||||
"name": "system.state",
|
||||
"values": [
|
||||
{
|
||||
"value": "on"
|
||||
},
|
||||
{
|
||||
"value": "off"
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
{
|
||||
"name": "any"
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
----
|
||||
|
||||
===== Class reference
|
||||
|
||||
The **class-reference** provider auto-completes classes available in the project. This
|
||||
provider supports these parameters:
|
||||
|
||||
[cols="1,1,2,4"]
|
||||
|===
|
||||
|Parameter |Type |Default value |Description
|
||||
|
||||
|`target`
|
||||
|`String` (`Class`)
|
||||
|_none_
|
||||
|The fully qualified name of the class that should be assignable to the chosen value.
|
||||
Typically used to filter out non candidate classes. Note that this information can
|
||||
be provided by the type itself by exposing a class with the appropriate upper bound.
|
||||
|
||||
|`concrete`
|
||||
|`boolean`
|
||||
|true
|
||||
|Specify if only concrete classes are to be considered as valid candidates.
|
||||
|===
|
||||
|
||||
|
||||
The meta-data snippet below corresponds to the standard `server.jsp-servlet.class-name`
|
||||
property that defines the `JspServlet` class name to use:
|
||||
|
||||
[source,json,indent=0]
|
||||
----
|
||||
{"hints": [
|
||||
{
|
||||
"name": "server.jsp-servlet.class-name",
|
||||
"providers": [
|
||||
{
|
||||
"name": "class-reference",
|
||||
"parameters": {
|
||||
"target": "javax.servlet.http.HttpServlet"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
----
|
||||
|
||||
===== Enum
|
||||
|
||||
The **enum** provider auto-completes the values of the `Enum` class referenced via the
|
||||
`target` parameter. This provider supports these parameters:
|
||||
|
||||
[cols="1,1,2,4"]
|
||||
|===
|
||||
|Parameter |Type |Default value |Description
|
||||
|
||||
| **`target`**
|
||||
| `String` (`Enum`)
|
||||
|_none_
|
||||
|The fully qualified name of the `Enum` class. This parameter is mandatory.
|
||||
|===
|
||||
|
||||
|
||||
The meta-data snippet below corresponds to the standard `spring.jooq.sql-dialect`
|
||||
property that defines the `SQLDialect` class name to use:
|
||||
|
||||
[source,json,indent=0]
|
||||
----
|
||||
{"hints": [
|
||||
{
|
||||
"name": "spring.jooq.sql-dialect",
|
||||
"providers": [
|
||||
{
|
||||
"name": "enum",
|
||||
"parameters": {
|
||||
"target": "org.jooq.SQLDialect"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
]}
|
||||
----
|
||||
|
||||
TIP: This is useful when you don't want your configuration classes to rely on classes
|
||||
that may not be on the classpath.
|
||||
|
||||
|
||||
===== Logger name
|
||||
|
||||
The **logger-name** provider auto-completes valid logger names. Typically, package and
|
||||
class names available in the current project can be auto-completed. Specific frameworks
|
||||
may have extra magic logger names that could be supported as well.
|
||||
|
||||
Since a logger name can be any arbitrary name, really, this provider should allow any
|
||||
value but could highlight valid packages and class names that are not available in the
|
||||
project's classpath.
|
||||
|
||||
The meta-data snippet below corresponds to the standard `logger.level` property, keys
|
||||
are _logger names_ and values correspond to the the standard log levels or any custom
|
||||
level:
|
||||
|
||||
[source,json,indent=0]
|
||||
----
|
||||
{"hints": [
|
||||
{
|
||||
"name": "logger.level.keys",
|
||||
"values": [
|
||||
{
|
||||
"value": "root",
|
||||
"description": "Root logger used to assign the default logging level."
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
{
|
||||
"name": "logger-name"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "logger.level.values",
|
||||
"values": [
|
||||
{
|
||||
"value": "trace"
|
||||
},
|
||||
{
|
||||
"value": "debug"
|
||||
},
|
||||
{
|
||||
"value": "info"
|
||||
},
|
||||
{
|
||||
"value": "warn"
|
||||
},
|
||||
{
|
||||
"value": "error"
|
||||
},
|
||||
{
|
||||
"value": "fatal"
|
||||
},
|
||||
{
|
||||
"value": "off"
|
||||
}
|
||||
|
||||
],
|
||||
"providers": [
|
||||
{
|
||||
"name": "any"
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
----
|
||||
|
||||
|
||||
===== Spring bean reference
|
||||
|
||||
The **spring-bean-reference** provider auto-completes the beans that are defined in
|
||||
the configuration of the current project. This provider supports these parameters:
|
||||
|
||||
[cols="1,1,2,4"]
|
||||
|===
|
||||
|Parameter |Type |Default value |Description
|
||||
|
||||
|`target`
|
||||
| `String` (`Class`)
|
||||
|_none_
|
||||
|The fully qualified name of the bean class that should be assignable to the candidate.
|
||||
Typically used to filter out non candidate beans.
|
||||
|===
|
||||
|
||||
The meta-data snippet below corresponds to the standard `spring.jmx.server` property
|
||||
that defines the name of the `MBeanServer` bean to use:
|
||||
|
||||
[source,json,indent=0]
|
||||
----
|
||||
{"hints": [
|
||||
{
|
||||
"name": "spring.jmx.server",
|
||||
"providers": [
|
||||
{
|
||||
"name": "spring-bean-reference",
|
||||
"parameters": {
|
||||
"target": "javax.management.MBeanServer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
----
|
||||
|
||||
[[configuration-metadata-annotation-processor]]
|
||||
=== Generating your own meta-data using the annotation processor
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provide hints on an {@link ItemMetadata}. Defines the list of possible values for a
|
||||
|
@ -27,7 +28,7 @@ import java.util.List;
|
|||
* <p>
|
||||
* The {@code name} of the hint is the name of the related property with one major
|
||||
* exception for map types as both the keys and values of the map can have hints. In such
|
||||
* a case, the hint should be suffixed by ".key" or ".values" respectively. Creating a
|
||||
* a case, the hint should be suffixed by ".keys" or ".values" respectively. Creating a
|
||||
* hint for a map using its property name is therefore invalid.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
|
@ -39,9 +40,12 @@ public class ItemHint implements Comparable<ItemHint> {
|
|||
|
||||
private final List<ValueHint> values;
|
||||
|
||||
public ItemHint(String name, List<ValueHint> values) {
|
||||
private final List<ProviderHint> providers;
|
||||
|
||||
public ItemHint(String name, List<ValueHint> values, List<ProviderHint> providers) {
|
||||
this.name = toCanonicalName(name);
|
||||
this.values = new ArrayList<ValueHint>(values);
|
||||
this.values = (values != null ? new ArrayList<ValueHint>(values) : new ArrayList<ValueHint>());
|
||||
this.providers = (providers != null ? new ArrayList<ProviderHint>(providers) : new ArrayList<ProviderHint>());
|
||||
}
|
||||
|
||||
private String toCanonicalName(String name) {
|
||||
|
@ -62,19 +66,23 @@ public class ItemHint implements Comparable<ItemHint> {
|
|||
return Collections.unmodifiableList(this.values);
|
||||
}
|
||||
|
||||
public List<ProviderHint> getProviders() {
|
||||
return Collections.unmodifiableList(this.providers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ItemHint other) {
|
||||
return getName().compareTo(other.getName());
|
||||
}
|
||||
|
||||
public static ItemHint newHint(String name, ValueHint... values) {
|
||||
return new ItemHint(name, Arrays.asList(values));
|
||||
return new ItemHint(name, Arrays.asList(values), Collections.<ProviderHint>emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ItemHint{" + "name='" + this.name + '\'' + ", values=" + this.values
|
||||
+ '}';
|
||||
return "ItemHint{" + "name='" + this.name + ", values=" + this.values
|
||||
+ "providers=" + this.providers + '}';
|
||||
}
|
||||
|
||||
public static class ValueHint {
|
||||
|
@ -104,4 +112,28 @@ public class ItemHint implements Comparable<ItemHint> {
|
|||
|
||||
}
|
||||
|
||||
public static class ProviderHint {
|
||||
private final String name;
|
||||
private final Map<String,Object> parameters;
|
||||
|
||||
public ProviderHint(String name, Map<String, Object> parameters) {
|
||||
this.name = name;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Provider{" + "name='" + this.name + ", parameters=" + this.parameters +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ import java.lang.reflect.Array;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
@ -103,6 +105,22 @@ public class JsonMarshaller {
|
|||
}
|
||||
jsonObject.put("values", valuesArray);
|
||||
}
|
||||
if (!hint.getProviders().isEmpty()) {
|
||||
JSONArray providersArray = new JSONArray();
|
||||
for (ItemHint.ProviderHint providerHint : hint.getProviders()) {
|
||||
JSONObject providerHintObject = new JSONOrderedObject();
|
||||
providerHintObject.put("name", providerHint.getName());
|
||||
if (providerHint.getParameters() != null && !providerHint.getParameters().isEmpty()) {
|
||||
JSONObject parametersObject = new JSONOrderedObject();
|
||||
for (Map.Entry<String, Object> entry : providerHint.getParameters().entrySet()) {
|
||||
parametersObject.put(entry.getKey(), extractItemValue(entry.getValue()));
|
||||
}
|
||||
providerHintObject.put("parameters", parametersObject);
|
||||
}
|
||||
providersArray.put(providerHintObject);
|
||||
}
|
||||
jsonObject.put("providers", providersArray);
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
|
@ -182,7 +200,14 @@ public class JsonMarshaller {
|
|||
values.add(toValueHint((JSONObject) valuesArray.get(i)));
|
||||
}
|
||||
}
|
||||
return new ItemHint(name, values);
|
||||
List<ItemHint.ProviderHint> providers = new ArrayList<ItemHint.ProviderHint>();
|
||||
if (object.has("providers")) {
|
||||
JSONArray providersObject = object.getJSONArray("providers");
|
||||
for (int i = 0; i < providersObject.length(); i++) {
|
||||
providers.add(toProviderHint((JSONObject) providersObject.get(i)));
|
||||
}
|
||||
}
|
||||
return new ItemHint(name, values, providers);
|
||||
}
|
||||
|
||||
private ItemHint.ValueHint toValueHint(JSONObject object) {
|
||||
|
@ -191,6 +216,20 @@ public class JsonMarshaller {
|
|||
return new ItemHint.ValueHint(value, description);
|
||||
}
|
||||
|
||||
private ItemHint.ProviderHint toProviderHint(JSONObject object) {
|
||||
String name = object.getString("name");
|
||||
Map<String,Object> parameters = new HashMap<String,Object>();
|
||||
if (object.has("parameters")) {
|
||||
JSONObject parametersObject = object.getJSONObject("parameters");
|
||||
for (Object k : parametersObject.keySet()) {
|
||||
String key = (String) k;
|
||||
Object value = readItemValue(parametersObject.get(key));
|
||||
parameters.put(key, value);
|
||||
}
|
||||
}
|
||||
return new ItemHint.ProviderHint(name, parameters);
|
||||
}
|
||||
|
||||
private Object readItemValue(Object value) {
|
||||
if (value instanceof JSONArray) {
|
||||
JSONArray array = (JSONArray) value;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue