Create RelaxedPropertyResolver
Create RelaxedPropertyResolver class that can be used to get values from another PropertyResolver (probably an Environment) using the same relaxed rules as the RelaxedDataBinder. The commit extracts the relaxed naming rules from RelaxedDataBinder into a new RelaxedNames class. Issue: #55621278pull/50/head
parent
d64a44547c
commit
0a7ac89984
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.bind;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Generates relaxed name variations from a given source.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see RelaxedDataBinder
|
||||
* @see RelaxedPropertyResolver
|
||||
*/
|
||||
public final class RelaxedNames implements Iterable<String> {
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Create a new {@link RelaxedNames} instance.
|
||||
*
|
||||
* @param name the source name. For the maximum number of variations specify the name
|
||||
* using dashed notation (e.g. {@literal my-property-name}
|
||||
*/
|
||||
public RelaxedNames(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new RelaxedNamesIterator();
|
||||
}
|
||||
|
||||
private class RelaxedNamesIterator implements Iterator<String> {
|
||||
|
||||
private int variation = 0;
|
||||
|
||||
private int manipulation = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (this.variation < Variation.values().length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
String result = RelaxedNames.this.name;
|
||||
result = Manipulation.values()[this.manipulation].apply(result);
|
||||
result = Variation.values()[this.variation].apply(result);
|
||||
this.manipulation++;
|
||||
if (this.manipulation >= Manipulation.values().length) {
|
||||
this.variation++;
|
||||
this.manipulation = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static enum Variation {
|
||||
NONE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
LOWERCASE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value.toLowerCase();
|
||||
}
|
||||
},
|
||||
UPPERCASE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value.toUpperCase();
|
||||
}
|
||||
};
|
||||
|
||||
public abstract String apply(String value);
|
||||
}
|
||||
|
||||
static enum Manipulation {
|
||||
NONE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
UNDERSCORE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value.replace("-", "_");
|
||||
}
|
||||
},
|
||||
CAMELCASE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String field : UNDERSCORE.apply(value).split("_")) {
|
||||
builder.append(builder.length() == 0 ? field : StringUtils
|
||||
.capitalize(field));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
};
|
||||
|
||||
public abstract String apply(String value);
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.bind;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.PropertyResolver;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* {@link PropertyResolver} that attempts to resolve values using {@link RelaxedNames}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see RelaxedNames
|
||||
*/
|
||||
public class RelaxedPropertyResolver implements PropertyResolver {
|
||||
|
||||
private final PropertyResolver resolver;
|
||||
|
||||
private final String prefix;
|
||||
|
||||
public RelaxedPropertyResolver(PropertyResolver resolver) {
|
||||
this(resolver, null);
|
||||
}
|
||||
|
||||
public RelaxedPropertyResolver(PropertyResolver resolver, String prefix) {
|
||||
Assert.notNull(resolver, "PropertyResolver must not be null");
|
||||
this.resolver = resolver;
|
||||
this.prefix = (prefix == null ? "" : prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredProperty(String key) throws IllegalStateException {
|
||||
return getRequiredProperty(key, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getRequiredProperty(String key, Class<T> targetType)
|
||||
throws IllegalStateException {
|
||||
T value = getProperty(key, targetType);
|
||||
Assert.state(value != null, format("required key [%s] not found", key));
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key) {
|
||||
return getProperty(key, String.class, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
return getProperty(key, String.class, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getProperty(String key, Class<T> targetType) {
|
||||
return getProperty(key, targetType, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
|
||||
for (String relaxedKey : new RelaxedNames(key)) {
|
||||
if (this.resolver.containsProperty(this.prefix + relaxedKey)) {
|
||||
return this.resolver.getProperty(this.prefix + relaxedKey, targetType,
|
||||
defaultValue);
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Class<T> getPropertyAsClass(String key, Class<T> targetType) {
|
||||
for (String relaxedKey : new RelaxedNames(key)) {
|
||||
if (this.resolver.containsProperty(this.prefix + relaxedKey)) {
|
||||
return this.resolver.getPropertyAsClass(this.prefix + relaxedKey,
|
||||
targetType);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsProperty(String key) {
|
||||
for (String relaxedKey : new RelaxedNames(key)) {
|
||||
if (this.resolver.containsProperty(this.prefix + relaxedKey)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolvePlaceholders(String text) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unable to resolve placeholders with relaxed properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveRequiredPlaceholders(String text)
|
||||
throws IllegalArgumentException {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unable to resolve placeholders with relaxed properties");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map of all values from all underlying properties that start with the
|
||||
* specified key. NOTE: this method can only be used in the underlying resolver is a
|
||||
* {@link ConfigurableEnvironment}.
|
||||
* @param keyPrefix the key prefix used to filter results
|
||||
* @return a map of all sub properties starting with the specified key prefix.
|
||||
* @see #getSubProperties(PropertySources, RelaxedNames)
|
||||
* @see #getSubProperties(PropertySources, String, RelaxedNames)
|
||||
*/
|
||||
public Map<String, Object> getSubProperties(String keyPrefix) {
|
||||
Assert.isInstanceOf(ConfigurableEnvironment.class, this.resolver,
|
||||
"SubProperties not available.");
|
||||
ConfigurableEnvironment env = (ConfigurableEnvironment) this.resolver;
|
||||
return getSubProperties(env.getPropertySources(), this.prefix, new RelaxedNames(
|
||||
keyPrefix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map of all values from the specified {@link PropertySources} that start
|
||||
* with a particular key.
|
||||
* @param propertySources the property sources to scan
|
||||
* @param keyPrefix the key prefixes to test
|
||||
* @return a map of all sub properties starting with the specified key prefixes.
|
||||
* @see #getSubProperties(PropertySources, String, RelaxedNames)
|
||||
*/
|
||||
public static Map<String, Object> getSubProperties(PropertySources propertySources,
|
||||
RelaxedNames keyPrefix) {
|
||||
return getSubProperties(propertySources, null, keyPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Map of all values from the specified {@link PropertySources} that start
|
||||
* with a particular key.
|
||||
* @param propertySources the property sources to scan
|
||||
* @param rootPrefix a root prefix to be prepended to the keyPrefex (can be
|
||||
* {@code null})
|
||||
* @param keyPrefix the key prefixes to test
|
||||
* @return a map of all sub properties starting with the specified key prefixes.
|
||||
* @see #getSubProperties(PropertySources, String, RelaxedNames)
|
||||
*/
|
||||
public static Map<String, Object> getSubProperties(PropertySources propertySources,
|
||||
String rootPrefix, RelaxedNames keyPrefix) {
|
||||
Map<String, Object> subProperties = new LinkedHashMap<String, Object>();
|
||||
for (PropertySource<?> source : propertySources) {
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
for (String name : ((EnumerablePropertySource<?>) source)
|
||||
.getPropertyNames()) {
|
||||
String key = getSubKey(name, rootPrefix, keyPrefix);
|
||||
if (key != null) {
|
||||
subProperties.put(key, source.getProperty(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(subProperties);
|
||||
}
|
||||
|
||||
private static String getSubKey(String name, String rootPrefix, RelaxedNames keyPrefix) {
|
||||
rootPrefix = (rootPrefix == null ? "" : rootPrefix);
|
||||
for (String candidateKeyPrefix : keyPrefix) {
|
||||
if (name.startsWith(rootPrefix + candidateKeyPrefix)) {
|
||||
return name.substring((rootPrefix + candidateKeyPrefix).length());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.bind;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RelaxedNames}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class RelaxedNamesTests {
|
||||
|
||||
@Test
|
||||
public void iterator() throws Exception {
|
||||
Iterator<String> iterator = new RelaxedNames("my-RELAXED-property").iterator();
|
||||
assertThat(iterator.next(), equalTo("my-RELAXED-property"));
|
||||
assertThat(iterator.next(), equalTo("my_RELAXED_property"));
|
||||
assertThat(iterator.next(), equalTo("myRELAXEDProperty"));
|
||||
assertThat(iterator.next(), equalTo("my-relaxed-property"));
|
||||
assertThat(iterator.next(), equalTo("my_relaxed_property"));
|
||||
assertThat(iterator.next(), equalTo("myrelaxedproperty"));
|
||||
assertThat(iterator.next(), equalTo("MY-RELAXED-PROPERTY"));
|
||||
assertThat(iterator.next(), equalTo("MY_RELAXED_PROPERTY"));
|
||||
assertThat(iterator.next(), equalTo("MYRELAXEDPROPERTY"));
|
||||
assertThat(iterator.hasNext(), equalTo(false));
|
||||
|
||||
iterator = new RelaxedNames("nes_ted").iterator();
|
||||
assertThat(iterator.next(), equalTo("nes_ted"));
|
||||
assertThat(iterator.next(), equalTo("nes_ted"));
|
||||
assertThat(iterator.next(), equalTo("nesTed"));
|
||||
assertThat(iterator.next(), equalTo("nes_ted"));
|
||||
assertThat(iterator.next(), equalTo("nes_ted"));
|
||||
assertThat(iterator.next(), equalTo("nested"));
|
||||
assertThat(iterator.next(), equalTo("NES_TED"));
|
||||
assertThat(iterator.next(), equalTo("NES_TED"));
|
||||
assertThat(iterator.next(), equalTo("NESTED"));
|
||||
assertThat(iterator.hasNext(), equalTo(false));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.bind;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RelaxedPropertyResolver}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class RelaxedPropertyResolverTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private StandardEnvironment environment;
|
||||
|
||||
private RelaxedPropertyResolver resolver;
|
||||
|
||||
private LinkedHashMap<String, Object> source;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.environment = new StandardEnvironment();
|
||||
this.source = new LinkedHashMap<String, Object>();
|
||||
this.source.put("myString", "value");
|
||||
this.source.put("myInteger", 123);
|
||||
this.source.put("myClass", "java.lang.String");
|
||||
this.environment.getPropertySources().addFirst(
|
||||
new MapPropertySource("test", this.source));
|
||||
this.resolver = new RelaxedPropertyResolver(this.environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void needsPropertyResolver() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("PropertyResolver must not be null");
|
||||
new RelaxedPropertyResolver(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequiredProperty() throws Exception {
|
||||
assertThat(this.resolver.getRequiredProperty("my-string"), equalTo("value"));
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("required key [my-missing] not found");
|
||||
this.resolver.getRequiredProperty("my-missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequiredPropertyWithType() throws Exception {
|
||||
assertThat(this.resolver.getRequiredProperty("my-integer", Integer.class),
|
||||
equalTo(123));
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("required key [my-missing] not found");
|
||||
this.resolver.getRequiredProperty("my-missing", Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProperty() throws Exception {
|
||||
assertThat(this.resolver.getProperty("my-string"), equalTo("value"));
|
||||
assertThat(this.resolver.getProperty("my-missing"), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyWithDefault() throws Exception {
|
||||
assertThat(this.resolver.getProperty("my-string", "a"), equalTo("value"));
|
||||
assertThat(this.resolver.getProperty("my-missing", "a"), equalTo("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyWithType() throws Exception {
|
||||
assertThat(this.resolver.getProperty("my-integer", Integer.class), equalTo(123));
|
||||
assertThat(this.resolver.getProperty("my-missing", Integer.class), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyWithTypeAndDefault() throws Exception {
|
||||
assertThat(this.resolver.getProperty("my-integer", Integer.class, 345),
|
||||
equalTo(123));
|
||||
assertThat(this.resolver.getProperty("my-missing", Integer.class, 345),
|
||||
equalTo(345));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyAsClass() throws Exception {
|
||||
assertThat(this.resolver.getPropertyAsClass("my-class", String.class),
|
||||
equalTo(String.class));
|
||||
assertThat(this.resolver.getPropertyAsClass("my-missing", String.class),
|
||||
nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void containsProperty() throws Exception {
|
||||
assertThat(this.resolver.containsProperty("my-string"), equalTo(true));
|
||||
assertThat(this.resolver.containsProperty("myString"), equalTo(true));
|
||||
assertThat(this.resolver.containsProperty("my_string"), equalTo(true));
|
||||
assertThat(this.resolver.containsProperty("my-missing"), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolverPlaceholder() throws Exception {
|
||||
this.thrown.expect(UnsupportedOperationException.class);
|
||||
this.resolver.resolvePlaceholders("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveRequiredPlaceholders() throws Exception {
|
||||
this.thrown.expect(UnsupportedOperationException.class);
|
||||
this.resolver.resolveRequiredPlaceholders("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixed() throws Exception {
|
||||
this.resolver = new RelaxedPropertyResolver(this.environment, "a.b.c.");
|
||||
this.source.put("a.b.c.d", "test");
|
||||
assertThat(this.resolver.containsProperty("d"), equalTo(true));
|
||||
assertThat(this.resolver.getProperty("d"), equalTo("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subProperties() throws Exception {
|
||||
this.source.put("x.y.my-sub.a.b", "1");
|
||||
this.source.put("x.y.mySub.a.c", "2");
|
||||
this.source.put("x.y.MY_SUB.a.d", "3");
|
||||
this.resolver = new RelaxedPropertyResolver(this.environment, "x.y.");
|
||||
Map<String, Object> subProperties = this.resolver.getSubProperties("my-sub.");
|
||||
assertThat(subProperties.size(), equalTo(3));
|
||||
assertThat(subProperties.get("a.b"), equalTo((Object) "1"));
|
||||
assertThat(subProperties.get("a.c"), equalTo((Object) "2"));
|
||||
assertThat(subProperties.get("a.d"), equalTo((Object) "3"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue