Expose hints for Maps

This commit fixes the json parser so that hints for maps is also made
available.

Closes gh-5152
This commit is contained in:
Stephane Nicoll 2016-02-17 15:25:03 +01:00
parent d5732afa67
commit b60e9e5cb9
6 changed files with 271 additions and 14 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@ -27,12 +27,34 @@ import java.util.List;
*/
class ConfigurationMetadataHint {
private static final String KEY_SUFFIX = ".keys";
private static final String VALUE_SUFFIX = ".values";
private String id;
private final List<ValueHint> valueHints = new ArrayList<ValueHint>();
private final List<ValueProvider> valueProviders = new ArrayList<ValueProvider>();
public boolean isMapKeyHints() {
return (this.id != null && this.id.endsWith(KEY_SUFFIX));
}
public boolean isMapValueHints() {
return (this.id != null && this.id.endsWith(VALUE_SUFFIX));
}
public String resolveId() {
if (isMapKeyHints()) {
return this.id.substring(0, this.id.length() - KEY_SUFFIX.length());
}
if (isMapValueHints()) {
return this.id.substring(0, this.id.length() - VALUE_SUFFIX.length());
}
return this.id;
}
public String getId() {
return this.id;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@ -17,7 +17,6 @@
package org.springframework.boot.configurationmetadata;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
@ -44,9 +43,7 @@ public class ConfigurationMetadataProperty implements Serializable {
private Object defaultValue;
private final List<ValueHint> valueHints = new ArrayList<ValueHint>();
private final List<ValueProvider> valueProviders = new ArrayList<ValueProvider>();
private final Hints hints = new Hints();
private Deprecation deprecation;
@ -136,14 +133,24 @@ public class ConfigurationMetadataProperty implements Serializable {
this.defaultValue = defaultValue;
}
/**
* Return the hints of this item.
* @return the hints
*/
public Hints getHints() {
return this.hints;
}
/**
* The list of well-defined values, if any. If no extra {@link ValueProvider provider}
* is specified, these values are to be considered a closed-set of the available
* values for this item.
* @return the value hints
* @see #getHints()
*/
@Deprecated
public List<ValueHint> getValueHints() {
return this.valueHints;
return this.hints.getValueHints();
}
/**
@ -151,9 +158,11 @@ public class ConfigurationMetadataProperty implements Serializable {
* {@link ValueProvider} is enabled for an item: the first in the list that is
* supported should be used.
* @return the value providers
* @see #getHints()
*/
@Deprecated
public List<ValueProvider> getValueProviders() {
return this.valueProviders;
return this.hints.getValueProviders();
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@ -127,8 +127,22 @@ public final class ConfigurationMetadataRepositoryJsonBuilder {
for (ConfigurationMetadataHint hint : metadata.getHints()) {
ConfigurationMetadataProperty property = allProperties.get(hint.getId());
if (property != null) {
property.getValueHints().addAll(hint.getValueHints());
property.getValueProviders().addAll(hint.getValueProviders());
property.getHints().getValueHints().addAll(hint.getValueHints());
property.getHints().getValueProviders().addAll(hint.getValueProviders());
}
else {
String id = hint.resolveId();
property = allProperties.get(id);
if (property != null) {
if (hint.isMapKeyHints()) {
property.getHints().getKeyHints().addAll(hint.getValueHints());
property.getHints().getKeyProviders().addAll(hint.getValueProviders());
}
else {
property.getHints().getValueHints().addAll(hint.getValueHints());
property.getHints().getValueProviders().addAll(hint.getValueProviders());
}
}
}
}
return repository;

View File

@ -0,0 +1,82 @@
/*
* Copyright 2012-2016 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
*
* http://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.configurationmetadata;
import java.util.ArrayList;
import java.util.List;
/**
* Hints of an item to provide the list of values and/or the name of the provider
* responsible to identify suitable values. If the type of the related item is
* a {@link java.util.Map} it can have both key and value hints.
*
* @author Stephane Nicoll
* @since 1.4.0
*/
public class Hints {
private final List<ValueHint> keyHints = new ArrayList<ValueHint>();
private final List<ValueProvider> keyProviders = new ArrayList<ValueProvider>();
private final List<ValueHint> valueHints = new ArrayList<ValueHint>();
private final List<ValueProvider> valueProviders = new ArrayList<ValueProvider>();
/**
* The list of well-defined keys, if any. Only applicable if the type of the related
* item is a {@link java.util.Map}. If no extra {@link ValueProvider provider}
* is specified, these values are to be considered a closed-set of the available
* keys for the map.
* @return the key hints
*/
public List<ValueHint> getKeyHints() {
return this.keyHints;
}
/**
* The value providers that are applicable to the keys of this item. Only applicable
* if the type of the related item is a {@link java.util.Map}. Only one
* {@link ValueProvider} is enabled for a key: the first in the list that is
* supported should be used.
* @return the key providers
*/
public List<ValueProvider> getKeyProviders() {
return this.keyProviders;
}
/**
* The list of well-defined values, if any. If no extra {@link ValueProvider provider}
* is specified, these values are to be considered a closed-set of the available
* values for this item.
* @return the value hints
*/
public List<ValueHint> getValueHints() {
return this.valueHints;
}
/**
* The value providers that are applicable to this item. Only one
* {@link ValueProvider} is enabled for an item: the first in the list that is
* supported should be used.
* @return the value providers
*/
public List<ValueProvider> getValueProviders() {
return this.valueProviders;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@ -55,6 +55,23 @@ public class ConfigurationMetadataRepositoryJsonBuilderTests
}
}
@Test
public void hintsOnMaps() throws IOException {
InputStream map = getInputStreamFor("map");
try {
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder
.create(map).build();
validateMap(repo);
assertThat(repo.getAllGroups()).hasSize(1);
contains(repo.getAllProperties(), "spring.map.first", "spring.map.second",
"spring.map.keys", "spring.map.values");
assertThat(repo.getAllProperties()).hasSize(4);
}
finally {
map.close();
}
}
@Test
public void severalRepositoriesNoConflict() throws IOException {
InputStream foo = getInputStreamFor("foo");
@ -182,10 +199,44 @@ public class ConfigurationMetadataRepositoryJsonBuilderTests
validatePropertyHints(repo.getAllProperties().get("spring.bar.counter"), 0, 0);
}
private void validateMap(ConfigurationMetadataRepository repo) {
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.map");
ConfigurationMetadataSource source = group.getSources().get("org.acme.Map");
contains(source.getProperties(), "spring.map.first", "spring.map.second",
"spring.map.keys", "spring.map.values");
assertThat(source.getProperties()).hasSize(4);
ConfigurationMetadataProperty first = repo.getAllProperties().get("spring.map.first");
assertThat(first.getHints().getKeyHints()).hasSize(2);
assertThat(first.getHints().getValueProviders()).hasSize(0);
assertThat(first.getHints().getKeyHints().get(0).getValue()).isEqualTo("one");
assertThat(first.getHints().getKeyHints().get(0).getDescription()).isEqualTo("First.");
assertThat(first.getHints().getKeyHints().get(1).getValue()).isEqualTo("two");
assertThat(first.getHints().getKeyHints().get(1).getDescription()).isEqualTo("Second.");
ConfigurationMetadataProperty second = repo.getAllProperties().get("spring.map.second");
assertThat(second.getHints().getValueHints()).hasSize(2);
assertThat(second.getHints().getValueProviders()).hasSize(0);
assertThat(second.getHints().getValueHints().get(0).getValue()).isEqualTo("42");
assertThat(second.getHints().getValueHints().get(0).getDescription()).isEqualTo("Choose me.");
assertThat(second.getHints().getValueHints().get(1).getValue()).isEqualTo("24");
assertThat(second.getHints().getValueHints().get(1).getDescription()).isNull();
ConfigurationMetadataProperty keys = repo.getAllProperties().get("spring.map.keys");
assertThat(keys.getHints().getValueHints()).hasSize(0);
assertThat(keys.getHints().getValueProviders()).hasSize(1);
assertThat(keys.getHints().getValueProviders().get(0).getName()).isEqualTo("any");
ConfigurationMetadataProperty values = repo.getAllProperties().get("spring.map.values");
assertThat(values.getHints().getValueHints()).hasSize(0);
assertThat(values.getHints().getValueProviders()).hasSize(1);
assertThat(values.getHints().getValueProviders().get(0).getName()).isEqualTo("handle-as");
assertThat(values.getHints().getValueProviders().get(0).getParameters()).hasSize(1);
assertThat(values.getHints().getValueProviders().get(0).getParameters().get("target"))
.isEqualTo("java.lang.Integer");
}
private void validatePropertyHints(ConfigurationMetadataProperty property,
int valueHints, int valueProviders) {
assertThat(property.getValueHints().size()).isEqualTo(valueHints);
assertThat(property.getValueHints().size()).isEqualTo(valueProviders);
assertThat(property.getHints().getValueHints().size()).isEqualTo(valueHints);
assertThat(property.getHints().getValueProviders().size()).isEqualTo(valueProviders);
}
private void contains(Map<String, ?> source, String... keys) {

View File

@ -0,0 +1,79 @@
{
"groups": [
{
"name": "spring.map",
"type": "org.acme.Map",
"sourceType": "org.acme.config.MapApp",
"sourceMethod": "map()",
"description": "This is Map."
}
],
"properties": [
{
"name": "spring.map.first",
"type": "java.util.Map<String,String>",
"sourceType": "org.acme.Map"
},
{
"name": "spring.map.second",
"type": "java.util.Map<String,String>",
"sourceType": "org.acme.Map"
},
{
"name": "spring.map.keys",
"type": "java.lang.String",
"sourceType": "org.acme.Map"
},
{
"name": "spring.map.values",
"type": "java.lang.String",
"sourceType": "org.acme.Map"
}
],
"hints": [
{
"name": "spring.map.first.keys",
"values": [
{
"value": "one",
"description": "First."
},
{
"value": "two",
"description": "Second."
}
]
},
{
"name": "spring.map.second.values",
"values": [
{
"value": "42",
"description": "Choose me."
},
{
"value": "24"
}
]
},
{
"name": "spring.map.keys",
"providers": [
{
"name": "any"
}
]
},
{
"name": "spring.map.values",
"providers": [
{
"name": "handle-as",
"parameters": {
"target": "java.lang.Integer"
}
}
]
}
]
}