Delete remaining old `bind` classes
Remove the last deprecated class from `org.springframework.boot.bind` See gh-9000
This commit is contained in:
parent
3f71b8453f
commit
19f166a56f
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.beans.PropertyEditor;
|
|
||||||
import java.beans.PropertyEditorSupport;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link PropertyEditor} for {@link InetAddress} objects.
|
|
||||||
*
|
|
||||||
* @author Dave Syer
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class InetAddressEditor extends PropertyEditorSupport {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAsText() {
|
|
||||||
return ((InetAddress) getValue()).getHostAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAsText(String text) throws IllegalArgumentException {
|
|
||||||
try {
|
|
||||||
setValue(InetAddress.getByName(text));
|
|
||||||
}
|
|
||||||
catch (UnknownHostException ex) {
|
|
||||||
throw new IllegalArgumentException("Cannot locate host", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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 org.springframework.beans.PropertyValue;
|
|
||||||
import org.springframework.core.env.PropertySource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link PropertyValue} that can provide information about its origin.
|
|
||||||
*
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
class OriginCapablePropertyValue extends PropertyValue {
|
|
||||||
|
|
||||||
private static final String ATTRIBUTE_PROPERTY_ORIGIN = "propertyOrigin";
|
|
||||||
|
|
||||||
private final PropertyOrigin origin;
|
|
||||||
|
|
||||||
OriginCapablePropertyValue(PropertyValue propertyValue) {
|
|
||||||
this(propertyValue.getName(), propertyValue.getValue(),
|
|
||||||
(PropertyOrigin) propertyValue.getAttribute(ATTRIBUTE_PROPERTY_ORIGIN));
|
|
||||||
}
|
|
||||||
|
|
||||||
OriginCapablePropertyValue(String name, Object value, String originName,
|
|
||||||
PropertySource<?> originSource) {
|
|
||||||
this(name, value, new PropertyOrigin(originSource, originName));
|
|
||||||
}
|
|
||||||
|
|
||||||
OriginCapablePropertyValue(String name, Object value, PropertyOrigin origin) {
|
|
||||||
super(name, value);
|
|
||||||
this.origin = origin;
|
|
||||||
setAttribute(ATTRIBUTE_PROPERTY_ORIGIN, origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PropertyOrigin getOrigin() {
|
|
||||||
return this.origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String name = this.origin != null ? this.origin.getName() : this.getName();
|
|
||||||
String source = this.origin.getSource() != null
|
|
||||||
? this.origin.getSource().getName() : "unknown";
|
|
||||||
return "'" + name + "' from '" + source + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PropertyOrigin getOrigin(PropertyValue propertyValue) {
|
|
||||||
if (propertyValue instanceof OriginCapablePropertyValue) {
|
|
||||||
return ((OriginCapablePropertyValue) propertyValue).getOrigin();
|
|
||||||
}
|
|
||||||
return new OriginCapablePropertyValue(propertyValue).getOrigin();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.Collection;
|
|
||||||
|
|
||||||
import org.springframework.util.PatternMatchUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link PropertyNamePatternsMatcher} that delegates to
|
|
||||||
* {@link PatternMatchUtils#simpleMatch(String[], String)}.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
class PatternPropertyNamePatternsMatcher implements PropertyNamePatternsMatcher {
|
|
||||||
|
|
||||||
private final String[] patterns;
|
|
||||||
|
|
||||||
PatternPropertyNamePatternsMatcher(Collection<String> patterns) {
|
|
||||||
this.patterns = (patterns == null ? new String[] {}
|
|
||||||
: patterns.toArray(new String[patterns.size()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(String propertyName) {
|
|
||||||
return PatternMatchUtils.simpleMatch(this.patterns, propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strategy interface used to check if a property name matches specific criteria.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @since 1.2.0
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
interface PropertyNamePatternsMatcher {
|
|
||||||
|
|
||||||
PropertyNamePatternsMatcher ALL = new PropertyNamePatternsMatcher() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(String propertyName) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
PropertyNamePatternsMatcher NONE = new PropertyNamePatternsMatcher() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(String propertyName) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return {@code true} of the property name matches.
|
|
||||||
* @param propertyName the property name
|
|
||||||
* @return {@code true} if the property name matches
|
|
||||||
*/
|
|
||||||
boolean matches(String propertyName);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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 org.springframework.core.env.PropertySource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The origin of a property, specifically its source and its name before any prefix was
|
|
||||||
* removed.
|
|
||||||
*
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @since 1.3.0
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class PropertyOrigin {
|
|
||||||
|
|
||||||
private final PropertySource<?> source;
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
PropertyOrigin(PropertySource<?> source, String name) {
|
|
||||||
this.name = name;
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PropertySource<?> getSource() {
|
|
||||||
return this.source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,261 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.Collection;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.springframework.beans.MutablePropertyValues;
|
|
||||||
import org.springframework.beans.PropertyValue;
|
|
||||||
import org.springframework.beans.PropertyValues;
|
|
||||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
|
||||||
import org.springframework.core.env.CompositePropertySource;
|
|
||||||
import org.springframework.core.env.EnumerablePropertySource;
|
|
||||||
import org.springframework.core.env.MutablePropertySources;
|
|
||||||
import org.springframework.core.env.PropertySource;
|
|
||||||
import org.springframework.core.env.PropertySources;
|
|
||||||
import org.springframework.core.env.PropertySourcesPropertyResolver;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.validation.DataBinder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link PropertyValues} implementation backed by a {@link PropertySources}, bridging
|
|
||||||
* the two abstractions and allowing (for instance) a regular {@link DataBinder} to be
|
|
||||||
* used with the latter.
|
|
||||||
*
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class PropertySourcesPropertyValues implements PropertyValues {
|
|
||||||
|
|
||||||
private static final Pattern COLLECTION_PROPERTY = Pattern
|
|
||||||
.compile("\\[(\\d+)\\](\\.\\S+)?");
|
|
||||||
|
|
||||||
private final PropertySources propertySources;
|
|
||||||
|
|
||||||
private final Collection<String> nonEnumerableFallbackNames;
|
|
||||||
|
|
||||||
private final PropertyNamePatternsMatcher includes;
|
|
||||||
|
|
||||||
private final Map<String, PropertyValue> propertyValues = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
private final ConcurrentHashMap<String, PropertySource<?>> collectionOwners = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private final boolean resolvePlaceholders;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new PropertyValues from the given PropertySources.
|
|
||||||
* @param propertySources a PropertySources instance
|
|
||||||
*/
|
|
||||||
public PropertySourcesPropertyValues(PropertySources propertySources) {
|
|
||||||
this(propertySources, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new PropertyValues from the given PropertySources that will optionally
|
|
||||||
* resolve placeholders.
|
|
||||||
* @param propertySources a PropertySources instance
|
|
||||||
* @param resolvePlaceholders {@code true} if placeholders should be resolved.
|
|
||||||
* @since 1.5.2
|
|
||||||
*/
|
|
||||||
public PropertySourcesPropertyValues(PropertySources propertySources,
|
|
||||||
boolean resolvePlaceholders) {
|
|
||||||
this(propertySources, (Collection<String>) null, PropertyNamePatternsMatcher.ALL,
|
|
||||||
resolvePlaceholders);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new PropertyValues from the given PropertySources.
|
|
||||||
* @param propertySources a PropertySources instance
|
|
||||||
* @param includePatterns property name patterns to include from system properties and
|
|
||||||
* environment variables
|
|
||||||
* @param nonEnumerableFallbackNames the property names to try in lieu of an
|
|
||||||
* {@link EnumerablePropertySource}.
|
|
||||||
*/
|
|
||||||
public PropertySourcesPropertyValues(PropertySources propertySources,
|
|
||||||
Collection<String> includePatterns,
|
|
||||||
Collection<String> nonEnumerableFallbackNames) {
|
|
||||||
this(propertySources, nonEnumerableFallbackNames,
|
|
||||||
new PatternPropertyNamePatternsMatcher(includePatterns), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new PropertyValues from the given PropertySources.
|
|
||||||
* @param propertySources a PropertySources instance
|
|
||||||
* @param nonEnumerableFallbackNames the property names to try in lieu of an
|
|
||||||
* {@link EnumerablePropertySource}.
|
|
||||||
* @param includes the property name patterns to include
|
|
||||||
* @param resolvePlaceholders flag to indicate the placeholders should be resolved
|
|
||||||
*/
|
|
||||||
PropertySourcesPropertyValues(PropertySources propertySources,
|
|
||||||
Collection<String> nonEnumerableFallbackNames,
|
|
||||||
PropertyNamePatternsMatcher includes, boolean resolvePlaceholders) {
|
|
||||||
Assert.notNull(propertySources, "PropertySources must not be null");
|
|
||||||
Assert.notNull(includes, "Includes must not be null");
|
|
||||||
MutablePropertySources mutablePropertySources = new MutablePropertySources(
|
|
||||||
propertySources);
|
|
||||||
mutablePropertySources.remove(ConfigurationPropertySources.PROPERTY_SOURCE_NAME);
|
|
||||||
propertySources = mutablePropertySources;
|
|
||||||
this.propertySources = propertySources;
|
|
||||||
this.nonEnumerableFallbackNames = nonEnumerableFallbackNames;
|
|
||||||
this.includes = includes;
|
|
||||||
this.resolvePlaceholders = resolvePlaceholders;
|
|
||||||
PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(
|
|
||||||
propertySources);
|
|
||||||
for (PropertySource<?> source : propertySources) {
|
|
||||||
processPropertySource(source, resolver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processPropertySource(PropertySource<?> source,
|
|
||||||
PropertySourcesPropertyResolver resolver) {
|
|
||||||
if (source instanceof CompositePropertySource) {
|
|
||||||
processCompositePropertySource((CompositePropertySource) source, resolver);
|
|
||||||
}
|
|
||||||
else if (source instanceof EnumerablePropertySource) {
|
|
||||||
processEnumerablePropertySource((EnumerablePropertySource<?>) source,
|
|
||||||
resolver, this.includes);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
processNonEnumerablePropertySource(source, resolver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processCompositePropertySource(CompositePropertySource source,
|
|
||||||
PropertySourcesPropertyResolver resolver) {
|
|
||||||
for (PropertySource<?> nested : source.getPropertySources()) {
|
|
||||||
processPropertySource(nested, resolver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processEnumerablePropertySource(EnumerablePropertySource<?> source,
|
|
||||||
PropertySourcesPropertyResolver resolver,
|
|
||||||
PropertyNamePatternsMatcher includes) {
|
|
||||||
if (source.getPropertyNames().length > 0) {
|
|
||||||
for (String propertyName : source.getPropertyNames()) {
|
|
||||||
if (includes.matches(propertyName)) {
|
|
||||||
Object value = getEnumerableProperty(source, resolver, propertyName);
|
|
||||||
putIfAbsent(propertyName, value, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object getEnumerableProperty(EnumerablePropertySource<?> source,
|
|
||||||
PropertySourcesPropertyResolver resolver, String propertyName) {
|
|
||||||
try {
|
|
||||||
if (this.resolvePlaceholders) {
|
|
||||||
return resolver.getProperty(propertyName, Object.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (RuntimeException ex) {
|
|
||||||
// Probably could not resolve placeholders, ignore it here
|
|
||||||
}
|
|
||||||
return source.getProperty(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processNonEnumerablePropertySource(PropertySource<?> source,
|
|
||||||
PropertySourcesPropertyResolver resolver) {
|
|
||||||
// We can only do exact matches for non-enumerable property names, but
|
|
||||||
// that's better than nothing...
|
|
||||||
if (this.nonEnumerableFallbackNames == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (String propertyName : this.nonEnumerableFallbackNames) {
|
|
||||||
if (!source.containsProperty(propertyName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Object value = null;
|
|
||||||
try {
|
|
||||||
value = resolver.getProperty(propertyName, Object.class);
|
|
||||||
}
|
|
||||||
catch (RuntimeException ex) {
|
|
||||||
// Probably could not convert to Object, weird, but ignorable
|
|
||||||
}
|
|
||||||
if (value == null) {
|
|
||||||
value = source.getProperty(propertyName.toUpperCase());
|
|
||||||
}
|
|
||||||
putIfAbsent(propertyName, value, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PropertyValue[] getPropertyValues() {
|
|
||||||
Collection<PropertyValue> values = this.propertyValues.values();
|
|
||||||
return values.toArray(new PropertyValue[values.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PropertyValue getPropertyValue(String propertyName) {
|
|
||||||
PropertyValue propertyValue = this.propertyValues.get(propertyName);
|
|
||||||
if (propertyValue != null) {
|
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
for (PropertySource<?> source : this.propertySources) {
|
|
||||||
Object value = source.getProperty(propertyName);
|
|
||||||
propertyValue = putIfAbsent(propertyName, value, source);
|
|
||||||
if (propertyValue != null) {
|
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PropertyValue putIfAbsent(String propertyName, Object value,
|
|
||||||
PropertySource<?> source) {
|
|
||||||
if (value != null && !this.propertyValues.containsKey(propertyName)) {
|
|
||||||
PropertySource<?> collectionOwner = this.collectionOwners.putIfAbsent(
|
|
||||||
COLLECTION_PROPERTY.matcher(propertyName).replaceAll("[]"), source);
|
|
||||||
if (collectionOwner == null || collectionOwner == source) {
|
|
||||||
PropertyValue propertyValue = new OriginCapablePropertyValue(propertyName,
|
|
||||||
value, propertyName, source);
|
|
||||||
this.propertyValues.put(propertyName, propertyValue);
|
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PropertyValues changesSince(PropertyValues old) {
|
|
||||||
MutablePropertyValues changes = new MutablePropertyValues();
|
|
||||||
// for each property value in the new set
|
|
||||||
for (PropertyValue newValue : getPropertyValues()) {
|
|
||||||
// if there wasn't an old one, add it
|
|
||||||
PropertyValue oldValue = old.getPropertyValue(newValue.getName());
|
|
||||||
if (oldValue == null || !oldValue.equals(newValue)) {
|
|
||||||
changes.addPropertyValue(newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(String propertyName) {
|
|
||||||
return getPropertyValue(propertyName) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return this.propertyValues.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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 org.springframework.beans.NotWritablePropertyException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A custom {@link NotWritablePropertyException} that is thrown when a failure occurs
|
|
||||||
* during relaxed binding.
|
|
||||||
*
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @since 1.3.0
|
|
||||||
* @see RelaxedDataBinder
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class RelaxedBindingNotWritablePropertyException
|
|
||||||
extends NotWritablePropertyException {
|
|
||||||
|
|
||||||
private final String message;
|
|
||||||
|
|
||||||
private final PropertyOrigin propertyOrigin;
|
|
||||||
|
|
||||||
RelaxedBindingNotWritablePropertyException(NotWritablePropertyException ex,
|
|
||||||
PropertyOrigin propertyOrigin) {
|
|
||||||
super(ex.getBeanClass(), ex.getPropertyName());
|
|
||||||
this.propertyOrigin = propertyOrigin;
|
|
||||||
this.message = "Failed to bind '" + propertyOrigin.getName() + "' from '"
|
|
||||||
+ propertyOrigin.getSource().getName() + "' to '" + ex.getPropertyName()
|
|
||||||
+ "' property on '" + ex.getBeanClass().getName() + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return this.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PropertyOrigin getPropertyOrigin() {
|
|
||||||
return this.propertyOrigin;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.EnumSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.core.convert.ConversionFailedException;
|
|
||||||
import org.springframework.core.convert.ConversionService;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.core.convert.converter.ConverterFactory;
|
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
|
||||||
import org.springframework.core.convert.support.GenericConversionService;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal {@link ConversionService} used by {@link RelaxedDataBinder} to support
|
|
||||||
* additional relaxed conversion.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @since 1.1.0
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
class RelaxedConversionService implements ConversionService {
|
|
||||||
|
|
||||||
private final ConversionService conversionService;
|
|
||||||
|
|
||||||
private final GenericConversionService additionalConverters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link RelaxedConversionService} instance.
|
|
||||||
* @param conversionService and option root conversion service
|
|
||||||
*/
|
|
||||||
RelaxedConversionService(ConversionService conversionService) {
|
|
||||||
this.conversionService = conversionService;
|
|
||||||
this.additionalConverters = new GenericConversionService();
|
|
||||||
DefaultConversionService.addCollectionConverters(this.additionalConverters);
|
|
||||||
this.additionalConverters
|
|
||||||
.addConverterFactory(new StringToEnumIgnoringCaseConverterFactory());
|
|
||||||
this.additionalConverters.addConverter(new StringToCharArrayConverter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
|
|
||||||
return (this.conversionService != null
|
|
||||||
&& this.conversionService.canConvert(sourceType, targetType))
|
|
||||||
|| this.additionalConverters.canConvert(sourceType, targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
|
||||||
return (this.conversionService != null
|
|
||||||
&& this.conversionService.canConvert(sourceType, targetType))
|
|
||||||
|| this.additionalConverters.canConvert(sourceType, targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T convert(Object source, Class<T> targetType) {
|
|
||||||
Assert.notNull(targetType, "The targetType to convert to cannot be null");
|
|
||||||
return (T) convert(source, TypeDescriptor.forObject(source),
|
|
||||||
TypeDescriptor.valueOf(targetType));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object convert(Object source, TypeDescriptor sourceType,
|
|
||||||
TypeDescriptor targetType) {
|
|
||||||
if (this.conversionService != null) {
|
|
||||||
try {
|
|
||||||
return this.conversionService.convert(source, sourceType, targetType);
|
|
||||||
}
|
|
||||||
catch (ConversionFailedException ex) {
|
|
||||||
// Ignore and try the additional converters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.additionalConverters.convert(source, sourceType, targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clone of Spring's package private StringToEnumConverterFactory, but ignoring the
|
|
||||||
* case of the source.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
private static class StringToEnumIgnoringCaseConverterFactory
|
|
||||||
implements ConverterFactory<String, Enum> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
|
|
||||||
Class<?> enumType = targetType;
|
|
||||||
while (enumType != null && !enumType.isEnum()) {
|
|
||||||
enumType = enumType.getSuperclass();
|
|
||||||
}
|
|
||||||
Assert.notNull(enumType, "The target type " + targetType.getName()
|
|
||||||
+ " does not refer to an enum");
|
|
||||||
return new StringToEnum(enumType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StringToEnum<T extends Enum> implements Converter<String, T> {
|
|
||||||
|
|
||||||
private final Class<T> enumType;
|
|
||||||
|
|
||||||
StringToEnum(Class<T> enumType) {
|
|
||||||
this.enumType = enumType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T convert(String source) {
|
|
||||||
if (source.isEmpty()) {
|
|
||||||
// It's an empty enum identifier: reset the enum value to null.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
source = source.trim();
|
|
||||||
for (T candidate : (Set<T>) EnumSet.allOf(this.enumType)) {
|
|
||||||
RelaxedNames names = new RelaxedNames(
|
|
||||||
candidate.name().replace('_', '-').toLowerCase());
|
|
||||||
for (String name : names) {
|
|
||||||
if (name.equals(source)) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (candidate.name().equalsIgnoreCase(source)) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("No enum constant "
|
|
||||||
+ this.enumType.getCanonicalName() + "." + source);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,722 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.net.InetAddress;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeanWrapper;
|
|
||||||
import org.springframework.beans.BeanWrapperImpl;
|
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
import org.springframework.beans.InvalidPropertyException;
|
|
||||||
import org.springframework.beans.MutablePropertyValues;
|
|
||||||
import org.springframework.beans.NotWritablePropertyException;
|
|
||||||
import org.springframework.beans.PropertyValue;
|
|
||||||
import org.springframework.core.convert.ConversionService;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.env.StandardEnvironment;
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.validation.AbstractPropertyBindingResult;
|
|
||||||
import org.springframework.validation.BeanPropertyBindingResult;
|
|
||||||
import org.springframework.validation.DataBinder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binder implementation that allows caller to bind to maps and also allows property names
|
|
||||||
* to match a bit loosely (if underscores or dashes are removed and replaced with camel
|
|
||||||
* case for example).
|
|
||||||
*
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @author Andy Wilkinson
|
|
||||||
* @see RelaxedNames
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class RelaxedDataBinder extends DataBinder {
|
|
||||||
|
|
||||||
private static final Object BLANK = new Object();
|
|
||||||
|
|
||||||
private String namePrefix;
|
|
||||||
|
|
||||||
private boolean ignoreNestedProperties;
|
|
||||||
|
|
||||||
private MultiValueMap<String, String> nameAliases = new LinkedMultiValueMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link RelaxedDataBinder} instance.
|
|
||||||
* @param target the target into which properties are bound
|
|
||||||
*/
|
|
||||||
public RelaxedDataBinder(Object target) {
|
|
||||||
super(wrapTarget(target));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link RelaxedDataBinder} instance.
|
|
||||||
* @param target the target into which properties are bound
|
|
||||||
* @param namePrefix An optional prefix to be used when reading properties
|
|
||||||
*/
|
|
||||||
public RelaxedDataBinder(Object target, String namePrefix) {
|
|
||||||
super(wrapTarget(target),
|
|
||||||
(StringUtils.hasLength(namePrefix) ? namePrefix : DEFAULT_OBJECT_NAME));
|
|
||||||
this.namePrefix = cleanNamePrefix(namePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String cleanNamePrefix(String namePrefix) {
|
|
||||||
if (!StringUtils.hasLength(namePrefix)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (namePrefix.endsWith(".") ? namePrefix : namePrefix + ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to disable binding of nested properties (i.e. those with period separators in
|
|
||||||
* their paths). Can be useful to disable this if the name prefix is empty and you
|
|
||||||
* don't want to ignore unknown fields.
|
|
||||||
* @param ignoreNestedProperties the flag to set (default false)
|
|
||||||
*/
|
|
||||||
public void setIgnoreNestedProperties(boolean ignoreNestedProperties) {
|
|
||||||
this.ignoreNestedProperties = ignoreNestedProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set name aliases.
|
|
||||||
* @param aliases a map of property name to aliases
|
|
||||||
*/
|
|
||||||
public void setNameAliases(Map<String, List<String>> aliases) {
|
|
||||||
this.nameAliases = new LinkedMultiValueMap<>(aliases);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add aliases to the {@link DataBinder}.
|
|
||||||
* @param name the property name to alias
|
|
||||||
* @param alias aliases for the property names
|
|
||||||
* @return this instance
|
|
||||||
*/
|
|
||||||
public RelaxedDataBinder withAlias(String name, String... alias) {
|
|
||||||
for (String value : alias) {
|
|
||||||
this.nameAliases.add(name, value);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBind(MutablePropertyValues propertyValues) {
|
|
||||||
super.doBind(modifyProperties(propertyValues, getTarget()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modify the property values so that period separated property paths are valid for
|
|
||||||
* map keys. Also creates new maps for properties of map type that are null (assuming
|
|
||||||
* all maps are potentially nested). The standard bracket {@code[...]} dereferencing
|
|
||||||
* is also accepted.
|
|
||||||
* @param propertyValues the property values
|
|
||||||
* @param target the target object
|
|
||||||
* @return modified property values
|
|
||||||
*/
|
|
||||||
private MutablePropertyValues modifyProperties(MutablePropertyValues propertyValues,
|
|
||||||
Object target) {
|
|
||||||
propertyValues = getPropertyValuesForNamePrefix(propertyValues);
|
|
||||||
if (target instanceof MapHolder) {
|
|
||||||
propertyValues = addMapPrefix(propertyValues);
|
|
||||||
}
|
|
||||||
BeanWrapper wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setConversionService(
|
|
||||||
new RelaxedConversionService(getConversionService()));
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
List<PropertyValue> sortedValues = new ArrayList<>();
|
|
||||||
Set<String> modifiedNames = new HashSet<>();
|
|
||||||
List<String> sortedNames = getSortedPropertyNames(propertyValues);
|
|
||||||
for (String name : sortedNames) {
|
|
||||||
PropertyValue propertyValue = propertyValues.getPropertyValue(name);
|
|
||||||
PropertyValue modifiedProperty = modifyProperty(wrapper, propertyValue);
|
|
||||||
if (modifiedNames.add(modifiedProperty.getName())) {
|
|
||||||
sortedValues.add(modifiedProperty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new MutablePropertyValues(sortedValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getSortedPropertyNames(MutablePropertyValues propertyValues) {
|
|
||||||
List<String> names = new LinkedList<>();
|
|
||||||
for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) {
|
|
||||||
names.add(propertyValue.getName());
|
|
||||||
}
|
|
||||||
sortPropertyNames(names);
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort by name so that parent properties get processed first (e.g. 'foo.bar' before
|
|
||||||
* 'foo.bar.spam'). Don't use Collections.sort() because the order might be
|
|
||||||
* significant for other property names (it shouldn't be but who knows what people
|
|
||||||
* might be relying on, e.g. HSQL has a JDBCXADataSource where "databaseName" is a
|
|
||||||
* synonym for "url").
|
|
||||||
* @param names the names to sort
|
|
||||||
*/
|
|
||||||
private void sortPropertyNames(List<String> names) {
|
|
||||||
for (String name : new ArrayList<>(names)) {
|
|
||||||
int propertyIndex = names.indexOf(name);
|
|
||||||
BeanPath path = new BeanPath(name);
|
|
||||||
for (String prefix : path.prefixes()) {
|
|
||||||
int prefixIndex = names.indexOf(prefix);
|
|
||||||
if (prefixIndex >= propertyIndex) {
|
|
||||||
// The child property has a parent in the list in the wrong order
|
|
||||||
names.remove(name);
|
|
||||||
names.add(prefixIndex, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MutablePropertyValues addMapPrefix(MutablePropertyValues propertyValues) {
|
|
||||||
MutablePropertyValues rtn = new MutablePropertyValues();
|
|
||||||
for (PropertyValue pv : propertyValues.getPropertyValues()) {
|
|
||||||
rtn.add("map." + pv.getName(), pv.getValue());
|
|
||||||
}
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MutablePropertyValues getPropertyValuesForNamePrefix(
|
|
||||||
MutablePropertyValues propertyValues) {
|
|
||||||
if (!StringUtils.hasText(this.namePrefix) && !this.ignoreNestedProperties) {
|
|
||||||
return propertyValues;
|
|
||||||
}
|
|
||||||
MutablePropertyValues rtn = new MutablePropertyValues();
|
|
||||||
for (PropertyValue value : propertyValues.getPropertyValues()) {
|
|
||||||
String name = value.getName();
|
|
||||||
for (String prefix : new RelaxedNames(stripLastDot(this.namePrefix))) {
|
|
||||||
for (String separator : new String[] { ".", "_" }) {
|
|
||||||
String candidate = (StringUtils.hasLength(prefix) ? prefix + separator
|
|
||||||
: prefix);
|
|
||||||
if (name.startsWith(candidate)) {
|
|
||||||
name = name.substring(candidate.length());
|
|
||||||
if (!(this.ignoreNestedProperties && name.contains("."))) {
|
|
||||||
PropertyOrigin propertyOrigin = OriginCapablePropertyValue
|
|
||||||
.getOrigin(value);
|
|
||||||
rtn.addPropertyValue(new OriginCapablePropertyValue(name,
|
|
||||||
value.getValue(), propertyOrigin));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String stripLastDot(String string) {
|
|
||||||
if (StringUtils.hasLength(string) && string.endsWith(".")) {
|
|
||||||
string = string.substring(0, string.length() - 1);
|
|
||||||
}
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PropertyValue modifyProperty(BeanWrapper target,
|
|
||||||
PropertyValue propertyValue) {
|
|
||||||
String name = propertyValue.getName();
|
|
||||||
String normalizedName = normalizePath(target, name);
|
|
||||||
if (!normalizedName.equals(name)) {
|
|
||||||
return new PropertyValue(normalizedName, propertyValue.getValue());
|
|
||||||
}
|
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize a bean property path to a format understood by a BeanWrapper. This is
|
|
||||||
* used so that
|
|
||||||
* <ul>
|
|
||||||
* <li>Fuzzy matching can be employed for bean property names</li>
|
|
||||||
* <li>Period separators can be used instead of indexing ([...]) for map keys</li>
|
|
||||||
* </ul>
|
|
||||||
* @param wrapper a bean wrapper for the object to bind
|
|
||||||
* @param path the bean path to bind
|
|
||||||
* @return a transformed path with correct bean wrapper syntax
|
|
||||||
*/
|
|
||||||
protected String normalizePath(BeanWrapper wrapper, String path) {
|
|
||||||
return initializePath(wrapper, new BeanPath(path), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
|
|
||||||
return new RelaxedBeanPropertyBindingResult(getTarget(), getObjectName(),
|
|
||||||
isAutoGrowNestedPaths(), getAutoGrowCollectionLimit(),
|
|
||||||
getConversionService());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String initializePath(BeanWrapper wrapper, BeanPath path, int index) {
|
|
||||||
String prefix = path.prefix(index);
|
|
||||||
String key = path.name(index);
|
|
||||||
if (path.isProperty(index)) {
|
|
||||||
key = getActualPropertyName(wrapper, prefix, key);
|
|
||||||
path.rename(index, key);
|
|
||||||
}
|
|
||||||
if (path.name(++index) == null) {
|
|
||||||
return path.toString();
|
|
||||||
}
|
|
||||||
String name = path.prefix(index);
|
|
||||||
TypeDescriptor descriptor = wrapper.getPropertyTypeDescriptor(name);
|
|
||||||
if (descriptor == null || descriptor.isMap()) {
|
|
||||||
if (isMapValueStringType(descriptor)
|
|
||||||
|| isBlanked(wrapper, name, path.name(index))) {
|
|
||||||
path.collapseKeys(index);
|
|
||||||
}
|
|
||||||
path.mapIndex(index);
|
|
||||||
extendMapIfNecessary(wrapper, path, index);
|
|
||||||
}
|
|
||||||
else if (descriptor.isCollection()) {
|
|
||||||
extendCollectionIfNecessary(wrapper, path, index);
|
|
||||||
}
|
|
||||||
else if (descriptor.getType().equals(Object.class)) {
|
|
||||||
if (isBlanked(wrapper, name, path.name(index))) {
|
|
||||||
path.collapseKeys(index);
|
|
||||||
}
|
|
||||||
path.mapIndex(index);
|
|
||||||
if (path.isLastNode(index)) {
|
|
||||||
wrapper.setPropertyValue(path.toString(), BLANK);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
String next = path.prefix(index + 1);
|
|
||||||
if (wrapper.getPropertyValue(next) == null) {
|
|
||||||
wrapper.setPropertyValue(next, new LinkedHashMap<String, Object>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return initializePath(wrapper, path, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMapValueStringType(TypeDescriptor descriptor) {
|
|
||||||
if (descriptor == null || descriptor.getMapValueTypeDescriptor() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Properties.class.isAssignableFrom(descriptor.getObjectType())) {
|
|
||||||
// Properties is declared as Map<Object,Object> but we know it's really
|
|
||||||
// Map<String,String>
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Class<?> valueType = descriptor.getMapValueTypeDescriptor().getObjectType();
|
|
||||||
return (valueType != null && CharSequence.class.isAssignableFrom(valueType));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private boolean isBlanked(BeanWrapper wrapper, String propertyName, String key) {
|
|
||||||
Object value = (wrapper.isReadableProperty(propertyName)
|
|
||||||
? wrapper.getPropertyValue(propertyName) : null);
|
|
||||||
if (value instanceof Map) {
|
|
||||||
if (((Map) value).get(key) == BLANK) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void extendCollectionIfNecessary(BeanWrapper wrapper, BeanPath path,
|
|
||||||
int index) {
|
|
||||||
String name = path.prefix(index);
|
|
||||||
TypeDescriptor elementDescriptor = wrapper.getPropertyTypeDescriptor(name)
|
|
||||||
.getElementTypeDescriptor();
|
|
||||||
if (!elementDescriptor.isMap() && !elementDescriptor.isCollection()
|
|
||||||
&& !elementDescriptor.getType().equals(Object.class)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object extend = new LinkedHashMap<String, Object>();
|
|
||||||
if (!elementDescriptor.isMap() && path.isArrayIndex(index)) {
|
|
||||||
extend = new ArrayList<>();
|
|
||||||
}
|
|
||||||
wrapper.setPropertyValue(path.prefix(index + 1), extend);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void extendMapIfNecessary(BeanWrapper wrapper, BeanPath path, int index) {
|
|
||||||
String name = path.prefix(index);
|
|
||||||
TypeDescriptor parent = wrapper.getPropertyTypeDescriptor(name);
|
|
||||||
if (parent == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TypeDescriptor descriptor = parent.getMapValueTypeDescriptor();
|
|
||||||
if (descriptor == null) {
|
|
||||||
descriptor = TypeDescriptor.valueOf(Object.class);
|
|
||||||
}
|
|
||||||
if (!descriptor.isMap() && !descriptor.isCollection()
|
|
||||||
&& !descriptor.getType().equals(Object.class)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String extensionName = path.prefix(index + 1);
|
|
||||||
if (wrapper.isReadableProperty(extensionName)) {
|
|
||||||
Object currentValue = wrapper.getPropertyValue(extensionName);
|
|
||||||
if ((descriptor.isCollection() && currentValue instanceof Collection)
|
|
||||||
|| (!descriptor.isCollection() && currentValue instanceof Map)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object extend = new LinkedHashMap<String, Object>();
|
|
||||||
if (descriptor.isCollection()) {
|
|
||||||
extend = new ArrayList<>();
|
|
||||||
}
|
|
||||||
if (descriptor.getType().equals(Object.class) && path.isLastNode(index)) {
|
|
||||||
extend = BLANK;
|
|
||||||
}
|
|
||||||
wrapper.setPropertyValue(extensionName, extend);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getActualPropertyName(BeanWrapper target, String prefix, String name) {
|
|
||||||
String propertyName = resolvePropertyName(target, prefix, name);
|
|
||||||
if (propertyName == null) {
|
|
||||||
propertyName = resolveNestedPropertyName(target, prefix, name);
|
|
||||||
}
|
|
||||||
return (propertyName == null ? name : propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveNestedPropertyName(BeanWrapper target, String prefix,
|
|
||||||
String name) {
|
|
||||||
StringBuilder candidate = new StringBuilder();
|
|
||||||
for (String field : name.split("[_\\-\\.]")) {
|
|
||||||
candidate.append(candidate.length() > 0 ? "." : "");
|
|
||||||
candidate.append(field);
|
|
||||||
String nested = resolvePropertyName(target, prefix, candidate.toString());
|
|
||||||
if (nested != null) {
|
|
||||||
Class<?> type = target.getPropertyType(nested);
|
|
||||||
if ((type != null) && Map.class.isAssignableFrom(type)) {
|
|
||||||
// Special case for map property (gh-3836).
|
|
||||||
return nested + "[" + name.substring(candidate.length() + 1) + "]";
|
|
||||||
}
|
|
||||||
String propertyName = resolvePropertyName(target,
|
|
||||||
joinString(prefix, nested),
|
|
||||||
name.substring(candidate.length() + 1));
|
|
||||||
if (propertyName != null) {
|
|
||||||
return joinString(nested, propertyName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolvePropertyName(BeanWrapper target, String prefix, String name) {
|
|
||||||
Iterable<String> names = getNameAndAliases(name);
|
|
||||||
for (String nameOrAlias : names) {
|
|
||||||
for (String candidate : new RelaxedNames(nameOrAlias)) {
|
|
||||||
try {
|
|
||||||
if (target.getPropertyType(joinString(prefix, candidate)) != null) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InvalidPropertyException ex) {
|
|
||||||
// swallow and continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String joinString(String prefix, String name) {
|
|
||||||
return (StringUtils.hasLength(prefix) ? prefix + "." + name : name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Iterable<String> getNameAndAliases(String name) {
|
|
||||||
List<String> aliases = this.nameAliases.get(name);
|
|
||||||
if (aliases == null) {
|
|
||||||
return Collections.singleton(name);
|
|
||||||
}
|
|
||||||
List<String> nameAndAliases = new ArrayList<>(aliases.size() + 1);
|
|
||||||
nameAndAliases.add(name);
|
|
||||||
nameAndAliases.addAll(aliases);
|
|
||||||
return nameAndAliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object wrapTarget(Object target) {
|
|
||||||
if (target instanceof Map) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Object> map = (Map<String, Object>) target;
|
|
||||||
target = new MapHolder(map);
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holder to allow Map targets to be bound.
|
|
||||||
*/
|
|
||||||
static class MapHolder {
|
|
||||||
|
|
||||||
private Map<String, Object> map;
|
|
||||||
|
|
||||||
MapHolder(Map<String, Object> map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMap(Map<String, Object> map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> getMap() {
|
|
||||||
return this.map;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A path though properties of a bean.
|
|
||||||
*/
|
|
||||||
private static class BeanPath {
|
|
||||||
|
|
||||||
private List<PathNode> nodes;
|
|
||||||
|
|
||||||
BeanPath(String path) {
|
|
||||||
this.nodes = splitPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> prefixes() {
|
|
||||||
List<String> prefixes = new ArrayList<>();
|
|
||||||
for (int index = 1; index < this.nodes.size(); index++) {
|
|
||||||
prefixes.add(prefix(index));
|
|
||||||
}
|
|
||||||
return prefixes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLastNode(int index) {
|
|
||||||
return index >= this.nodes.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<PathNode> splitPath(String path) {
|
|
||||||
List<PathNode> nodes = new ArrayList<>();
|
|
||||||
String current = extractIndexedPaths(path, nodes);
|
|
||||||
for (String name : StringUtils.delimitedListToStringArray(current, ".")) {
|
|
||||||
if (StringUtils.hasText(name)) {
|
|
||||||
nodes.add(new PropertyNode(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String extractIndexedPaths(String path, List<PathNode> nodes) {
|
|
||||||
int startRef = path.indexOf("[");
|
|
||||||
String current = path;
|
|
||||||
while (startRef >= 0) {
|
|
||||||
if (startRef > 0) {
|
|
||||||
nodes.addAll(splitPath(current.substring(0, startRef)));
|
|
||||||
}
|
|
||||||
int endRef = current.indexOf("]", startRef);
|
|
||||||
if (endRef > 0) {
|
|
||||||
String sub = current.substring(startRef + 1, endRef);
|
|
||||||
if (sub.matches("[0-9]+")) {
|
|
||||||
nodes.add(new ArrayIndexNode(sub));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nodes.add(new MapIndexNode(sub));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current = current.substring(endRef + 1);
|
|
||||||
startRef = current.indexOf("[");
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void collapseKeys(int index) {
|
|
||||||
List<PathNode> revised = new ArrayList<>();
|
|
||||||
for (int i = 0; i < index; i++) {
|
|
||||||
revised.add(this.nodes.get(i));
|
|
||||||
}
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (int i = index; i < this.nodes.size(); i++) {
|
|
||||||
if (i > index) {
|
|
||||||
builder.append(".");
|
|
||||||
}
|
|
||||||
builder.append(this.nodes.get(i).name);
|
|
||||||
}
|
|
||||||
revised.add(new PropertyNode(builder.toString()));
|
|
||||||
this.nodes = revised;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mapIndex(int index) {
|
|
||||||
PathNode node = this.nodes.get(index);
|
|
||||||
if (node instanceof PropertyNode) {
|
|
||||||
node = ((PropertyNode) node).mapIndex();
|
|
||||||
}
|
|
||||||
this.nodes.set(index, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String prefix(int index) {
|
|
||||||
return range(0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void rename(int index, String name) {
|
|
||||||
this.nodes.get(index).name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String name(int index) {
|
|
||||||
if (index < this.nodes.size()) {
|
|
||||||
return this.nodes.get(index).name;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String range(int start, int end) {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (int i = start; i < end; i++) {
|
|
||||||
PathNode node = this.nodes.get(i);
|
|
||||||
builder.append(node);
|
|
||||||
}
|
|
||||||
if (builder.toString().startsWith(("."))) {
|
|
||||||
builder.replace(0, 1, "");
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isArrayIndex(int index) {
|
|
||||||
return this.nodes.get(index) instanceof ArrayIndexNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isProperty(int index) {
|
|
||||||
return this.nodes.get(index) instanceof PropertyNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return prefix(this.nodes.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PathNode {
|
|
||||||
|
|
||||||
protected String name;
|
|
||||||
|
|
||||||
PathNode(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ArrayIndexNode extends PathNode {
|
|
||||||
|
|
||||||
ArrayIndexNode(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "[" + this.name + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MapIndexNode extends PathNode {
|
|
||||||
|
|
||||||
MapIndexNode(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "[" + this.name + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PropertyNode extends PathNode {
|
|
||||||
|
|
||||||
PropertyNode(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapIndexNode mapIndex() {
|
|
||||||
return new MapIndexNode(this.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "." + this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extended version of {@link BeanPropertyBindingResult} to support relaxed binding.
|
|
||||||
*/
|
|
||||||
private static class RelaxedBeanPropertyBindingResult
|
|
||||||
extends BeanPropertyBindingResult {
|
|
||||||
|
|
||||||
private RelaxedConversionService conversionService;
|
|
||||||
|
|
||||||
RelaxedBeanPropertyBindingResult(Object target, String objectName,
|
|
||||||
boolean autoGrowNestedPaths, int autoGrowCollectionLimit,
|
|
||||||
ConversionService conversionService) {
|
|
||||||
super(target, objectName, autoGrowNestedPaths, autoGrowCollectionLimit);
|
|
||||||
this.conversionService = new RelaxedConversionService(conversionService);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BeanWrapper createBeanWrapper() {
|
|
||||||
BeanWrapper beanWrapper = new RelaxedBeanWrapper(getTarget());
|
|
||||||
beanWrapper.setConversionService(this.conversionService);
|
|
||||||
beanWrapper.registerCustomEditor(InetAddress.class, new InetAddressEditor());
|
|
||||||
return beanWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extended version of {@link BeanWrapperImpl} to support relaxed binding.
|
|
||||||
*/
|
|
||||||
private static class RelaxedBeanWrapper extends BeanWrapperImpl {
|
|
||||||
|
|
||||||
private static final Set<String> BENIGN_PROPERTY_SOURCE_NAMES;
|
|
||||||
|
|
||||||
static {
|
|
||||||
Set<String> names = new HashSet<>();
|
|
||||||
names.add(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
|
|
||||||
names.add(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
|
||||||
BENIGN_PROPERTY_SOURCE_NAMES = Collections.unmodifiableSet(names);
|
|
||||||
}
|
|
||||||
|
|
||||||
RelaxedBeanWrapper(Object target) {
|
|
||||||
super(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPropertyValue(PropertyValue pv) throws BeansException {
|
|
||||||
try {
|
|
||||||
super.setPropertyValue(pv);
|
|
||||||
}
|
|
||||||
catch (NotWritablePropertyException ex) {
|
|
||||||
PropertyOrigin origin = OriginCapablePropertyValue.getOrigin(pv);
|
|
||||||
if (isBenign(origin)) {
|
|
||||||
logger.debug("Ignoring benign property binding failure", ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (origin == null) {
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
throw new RelaxedBindingNotWritablePropertyException(ex, origin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBenign(PropertyOrigin origin) {
|
|
||||||
String name = (origin == null ? null : origin.getSource().getName());
|
|
||||||
return BENIGN_PROPERTY_SOURCE_NAMES.contains(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,259 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates relaxed name variations from a given source.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Dave Syer
|
|
||||||
* @see RelaxedDataBinder
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public final class RelaxedNames implements Iterable<String> {
|
|
||||||
|
|
||||||
private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("([^A-Z-])([A-Z])");
|
|
||||||
|
|
||||||
private static final Pattern SEPARATED_TO_CAMEL_CASE_PATTERN = Pattern
|
|
||||||
.compile("[_\\-.]");
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private final Set<String> values = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 == null ? "" : name);
|
|
||||||
initialize(RelaxedNames.this.name, this.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<String> iterator() {
|
|
||||||
return this.values.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize(String name, Set<String> values) {
|
|
||||||
if (values.contains(name)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (Variation variation : Variation.values()) {
|
|
||||||
for (Manipulation manipulation : Manipulation.values()) {
|
|
||||||
String result = name;
|
|
||||||
result = manipulation.apply(result);
|
|
||||||
result = variation.apply(result);
|
|
||||||
values.add(result);
|
|
||||||
initialize(result, values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name variations.
|
|
||||||
*/
|
|
||||||
enum Variation {
|
|
||||||
|
|
||||||
NONE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
LOWERCASE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return value.isEmpty() ? value : value.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
UPPERCASE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return value.isEmpty() ? value : value.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
public abstract String apply(String value);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name manipulations.
|
|
||||||
*/
|
|
||||||
enum Manipulation {
|
|
||||||
|
|
||||||
NONE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
HYPHEN_TO_UNDERSCORE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return value.indexOf('-') != -1 ? value.replace('-', '_') : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
UNDERSCORE_TO_PERIOD {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return value.indexOf('_') != -1 ? value.replace('_', '.') : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
PERIOD_TO_UNDERSCORE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return value.indexOf('.') != -1 ? value.replace('.', '_') : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
CAMELCASE_TO_UNDERSCORE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
Matcher matcher = CAMEL_CASE_PATTERN.matcher(value);
|
|
||||||
if (!matcher.find()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
matcher = matcher.reset();
|
|
||||||
StringBuffer result = new StringBuffer();
|
|
||||||
while (matcher.find()) {
|
|
||||||
matcher.appendReplacement(result, matcher.group(1) + '_'
|
|
||||||
+ StringUtils.uncapitalize(matcher.group(2)));
|
|
||||||
}
|
|
||||||
matcher.appendTail(result);
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
CAMELCASE_TO_HYPHEN {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
Matcher matcher = CAMEL_CASE_PATTERN.matcher(value);
|
|
||||||
if (!matcher.find()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
matcher = matcher.reset();
|
|
||||||
StringBuffer result = new StringBuffer();
|
|
||||||
while (matcher.find()) {
|
|
||||||
matcher.appendReplacement(result, matcher.group(1) + '-'
|
|
||||||
+ StringUtils.uncapitalize(matcher.group(2)));
|
|
||||||
}
|
|
||||||
matcher.appendTail(result);
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
SEPARATED_TO_CAMELCASE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return separatedToCamelCase(value, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
CASE_INSENSITIVE_SEPARATED_TO_CAMELCASE {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(String value) {
|
|
||||||
return separatedToCamelCase(value, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final char[] SUFFIXES = new char[] { '_', '-', '.' };
|
|
||||||
|
|
||||||
public abstract String apply(String value);
|
|
||||||
|
|
||||||
private static String separatedToCamelCase(String value,
|
|
||||||
boolean caseInsensitive) {
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (String field : SEPARATED_TO_CAMEL_CASE_PATTERN.split(value)) {
|
|
||||||
field = (caseInsensitive ? field.toLowerCase() : field);
|
|
||||||
builder.append(
|
|
||||||
builder.length() == 0 ? field : StringUtils.capitalize(field));
|
|
||||||
}
|
|
||||||
char lastChar = value.charAt(value.length() - 1);
|
|
||||||
for (char suffix : SUFFIXES) {
|
|
||||||
if (lastChar == suffix) {
|
|
||||||
builder.append(suffix);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a {@link RelaxedNames} for the given source camelCase source name.
|
|
||||||
* @param name the source name in camelCase
|
|
||||||
* @return the relaxed names
|
|
||||||
*/
|
|
||||||
public static RelaxedNames forCamelCase(String name) {
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
for (char c : name.toCharArray()) {
|
|
||||||
result.append(Character.isUpperCase(c) && result.length() > 0
|
|
||||||
&& result.charAt(result.length() - 1) != '-'
|
|
||||||
? "-" + Character.toLowerCase(c) : c);
|
|
||||||
}
|
|
||||||
return new RelaxedNames(result.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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 org.springframework.core.convert.converter.Converter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a String to a Char Array.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
class StringToCharArrayConverter implements Converter<String, char[]> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] convert(String source) {
|
|
||||||
return source.toCharArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.nio.charset.Charset;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
import org.yaml.snakeyaml.constructor.Constructor;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.context.MessageSource;
|
|
||||||
import org.springframework.context.MessageSourceAware;
|
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.StreamUtils;
|
|
||||||
import org.springframework.validation.BeanPropertyBindingResult;
|
|
||||||
import org.springframework.validation.BindException;
|
|
||||||
import org.springframework.validation.BindingResult;
|
|
||||||
import org.springframework.validation.ObjectError;
|
|
||||||
import org.springframework.validation.Validator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate some YAML by binding it to an object of a specified type and then optionally
|
|
||||||
* running a {@link Validator} over it.
|
|
||||||
*
|
|
||||||
* @param <T> the configuration type
|
|
||||||
* @author Luke Taylor
|
|
||||||
* @author Dave Syer
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class YamlConfigurationFactory<T>
|
|
||||||
implements FactoryBean<T>, MessageSourceAware, InitializingBean {
|
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(YamlConfigurationFactory.class);
|
|
||||||
|
|
||||||
private final Class<?> type;
|
|
||||||
|
|
||||||
private String yaml;
|
|
||||||
|
|
||||||
private Resource resource;
|
|
||||||
|
|
||||||
private T configuration;
|
|
||||||
|
|
||||||
private Validator validator;
|
|
||||||
|
|
||||||
private MessageSource messageSource;
|
|
||||||
|
|
||||||
private Map<Class<?>, Map<String, String>> propertyAliases = Collections.emptyMap();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a validation constructor which will be applied to the YAML doc to see whether
|
|
||||||
* it matches the expected JavaBean.
|
|
||||||
* @param type the root type
|
|
||||||
*/
|
|
||||||
public YamlConfigurationFactory(Class<?> type) {
|
|
||||||
Assert.notNull(type, "Root type must not be null");
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the message source.
|
|
||||||
* @param messageSource the message source
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setMessageSource(MessageSource messageSource) {
|
|
||||||
this.messageSource = messageSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the property aliases.
|
|
||||||
* @param propertyAliases the property aliases
|
|
||||||
*/
|
|
||||||
public void setPropertyAliases(Map<Class<?>, Map<String, String>> propertyAliases) {
|
|
||||||
this.propertyAliases = new HashMap<>(propertyAliases);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the YAML.
|
|
||||||
* @param yaml the YAML
|
|
||||||
*/
|
|
||||||
public void setYaml(String yaml) {
|
|
||||||
this.yaml = yaml;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the resource.
|
|
||||||
* @param resource the resource
|
|
||||||
*/
|
|
||||||
public void setResource(Resource resource) {
|
|
||||||
this.resource = resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the validator.
|
|
||||||
* @param validator the validator
|
|
||||||
*/
|
|
||||||
public void setValidator(Validator validator) {
|
|
||||||
this.validator = validator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
|
||||||
if (this.yaml == null) {
|
|
||||||
Assert.state(this.resource != null, "Resource should not be null");
|
|
||||||
this.yaml = StreamUtils.copyToString(this.resource.getInputStream(),
|
|
||||||
Charset.defaultCharset());
|
|
||||||
}
|
|
||||||
Assert.state(this.yaml != null, "Yaml document should not be null: "
|
|
||||||
+ "either set it directly or set the resource to load it from");
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace(String.format("Yaml document is %n%s", this.yaml));
|
|
||||||
}
|
|
||||||
Constructor constructor = new YamlJavaBeanPropertyConstructor(this.type,
|
|
||||||
this.propertyAliases);
|
|
||||||
this.configuration = (T) (new Yaml(constructor)).load(this.yaml);
|
|
||||||
if (this.validator != null) {
|
|
||||||
validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validate() throws BindException {
|
|
||||||
BindingResult errors = new BeanPropertyBindingResult(this.configuration,
|
|
||||||
"configuration");
|
|
||||||
this.validator.validate(this.configuration, errors);
|
|
||||||
if (errors.hasErrors()) {
|
|
||||||
logger.error("YAML configuration failed validation");
|
|
||||||
for (ObjectError error : errors.getAllErrors()) {
|
|
||||||
logger.error(getErrorMessage(error));
|
|
||||||
}
|
|
||||||
throw new BindException(errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object getErrorMessage(ObjectError error) {
|
|
||||||
if (this.messageSource != null) {
|
|
||||||
Locale locale = Locale.getDefault();
|
|
||||||
return this.messageSource.getMessage(error, locale) + " (" + error + ")";
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getObjectType() {
|
|
||||||
if (this.configuration == null) {
|
|
||||||
return Object.class;
|
|
||||||
}
|
|
||||||
return this.configuration.getClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSingleton() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T getObject() throws Exception {
|
|
||||||
if (this.configuration == null) {
|
|
||||||
afterPropertiesSet();
|
|
||||||
}
|
|
||||||
return this.configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.beans.IntrospectionException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.yaml.snakeyaml.constructor.Constructor;
|
|
||||||
import org.yaml.snakeyaml.introspector.Property;
|
|
||||||
import org.yaml.snakeyaml.introspector.PropertyUtils;
|
|
||||||
import org.yaml.snakeyaml.nodes.NodeId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extended version of snakeyaml's Constructor class to facilitate mapping custom YAML
|
|
||||||
* keys to JavaBean property names.
|
|
||||||
*
|
|
||||||
* @author Luke Taylor
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class YamlJavaBeanPropertyConstructor extends Constructor {
|
|
||||||
|
|
||||||
private final Map<Class<?>, Map<String, Property>> properties = new HashMap<>();
|
|
||||||
|
|
||||||
private final PropertyUtils propertyUtils = new PropertyUtils();
|
|
||||||
|
|
||||||
public YamlJavaBeanPropertyConstructor(Class<?> theRoot) {
|
|
||||||
super(theRoot);
|
|
||||||
this.yamlClassConstructors.put(NodeId.mapping,
|
|
||||||
new CustomPropertyConstructMapping());
|
|
||||||
}
|
|
||||||
|
|
||||||
public YamlJavaBeanPropertyConstructor(Class<?> theRoot,
|
|
||||||
Map<Class<?>, Map<String, String>> propertyAliases) {
|
|
||||||
this(theRoot);
|
|
||||||
for (Class<?> key : propertyAliases.keySet()) {
|
|
||||||
Map<String, String> map = propertyAliases.get(key);
|
|
||||||
if (map != null) {
|
|
||||||
for (String alias : map.keySet()) {
|
|
||||||
addPropertyAlias(alias, key, map.get(alias));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an alias for a JavaBean property name on a particular type. The values of YAML
|
|
||||||
* keys with the alias name will be mapped to the JavaBean property.
|
|
||||||
* @param alias the alias to map
|
|
||||||
* @param type the type of property
|
|
||||||
* @param name the property name
|
|
||||||
*/
|
|
||||||
protected final void addPropertyAlias(String alias, Class<?> type, String name) {
|
|
||||||
Map<String, Property> typeMap = this.properties.get(type);
|
|
||||||
if (typeMap == null) {
|
|
||||||
typeMap = new HashMap<>();
|
|
||||||
this.properties.put(type, typeMap);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
typeMap.put(alias, this.propertyUtils.getProperty(type, name));
|
|
||||||
}
|
|
||||||
catch (IntrospectionException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom {@code ConstructMapping} to resolve properties.
|
|
||||||
*/
|
|
||||||
class CustomPropertyConstructMapping extends ConstructMapping {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Property getProperty(Class<?> type, String name)
|
|
||||||
throws IntrospectionException {
|
|
||||||
Map<String, Property> forType = YamlJavaBeanPropertyConstructor.this.properties
|
|
||||||
.get(type);
|
|
||||||
Property property = (forType == null ? null : forType.get(name));
|
|
||||||
return (property == null ? super.getProperty(type, name) : property);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2014 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classes and utilities to help when binding spring based configuration files to objects.
|
|
||||||
*
|
|
||||||
* @see org.springframework.boot.bind.RelaxedDataBinder
|
|
||||||
* @see org.springframework.boot.bind.YamlConfigurationFactory
|
|
||||||
*/
|
|
||||||
package org.springframework.boot.bind;
|
|
|
@ -1,347 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.ArrayList;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeanWrapperImpl;
|
|
||||||
import org.springframework.boot.bind.RelaxedDataBinderTests.TargetWithNestedObject;
|
|
||||||
import org.springframework.context.expression.MapAccessor;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.expression.Expression;
|
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Dave Syer
|
|
||||||
*/
|
|
||||||
public class BindingPreparationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBeanWrapperCreatesNewMaps() throws Exception {
|
|
||||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
// For a nested map, you only have to get an element of it for it to be created
|
|
||||||
wrapper.getPropertyValue("nested[foo]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBeanWrapperCreatesNewMapEntries() throws Exception {
|
|
||||||
TargetWithNestedMapOfBean target = new TargetWithNestedMapOfBean();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
// For a nested map, you only have to get an element of it for it to be created
|
|
||||||
wrapper.getPropertyValue("nested[foo]");
|
|
||||||
wrapper.setPropertyValue("nested[foo].foo", "bar");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testListOfBeansWithList() throws Exception {
|
|
||||||
TargetWithNestedListOfBeansWithList target = new TargetWithNestedListOfBeansWithList();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
binder.normalizePath(wrapper, "nested[0].list[1]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[0].list[1]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testListOfBeansWithListAndNoPeriod() throws Exception {
|
|
||||||
TargetWithNestedListOfBeansWithList target = new TargetWithNestedListOfBeansWithList();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
binder.normalizePath(wrapper, "nested[0]list[1]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[0].list[1]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowWithFuzzyNameCapitals() throws Exception {
|
|
||||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
String result = binder.normalizePath(wrapper, "NESTED[foo][bar]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat("nested[foo][bar]").isEqualTo(result);
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo][bar]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowWithFuzzyNameUnderscores() throws Exception {
|
|
||||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
String result = binder.normalizePath(wrapper, "nes_ted[foo][bar]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat("nested[foo][bar]").isEqualTo(result);
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo][bar]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowNewNestedMapOfMaps() throws Exception {
|
|
||||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
String result = binder.normalizePath(wrapper, "nested[foo][bar]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat("nested[foo][bar]").isEqualTo(result);
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo][bar]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowNewNestedMapOfBeans() throws Exception {
|
|
||||||
TargetWithNestedMapOfBean target = new TargetWithNestedMapOfBean();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
String result = binder.normalizePath(wrapper, "nested[foo].foo");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat("nested[foo].foo").isEqualTo(result);
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowNewNestedMapOfBeansWithPeriod() throws Exception {
|
|
||||||
TargetWithNestedMapOfBean target = new TargetWithNestedMapOfBean();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
String result = binder.normalizePath(wrapper, "nested.foo.foo");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat("nested[foo].foo").isEqualTo(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowNewNestedMapOfListOfString() throws Exception {
|
|
||||||
TargetWithNestedMapOfListOfString target = new TargetWithNestedMapOfListOfString();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
binder.normalizePath(wrapper, "nested[foo][0]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowListOfMaps() throws Exception {
|
|
||||||
TargetWithNestedListOfMaps target = new TargetWithNestedListOfMaps();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
binder.normalizePath(wrapper, "nested[0][foo]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[0]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutoGrowListOfLists() throws Exception {
|
|
||||||
TargetWithNestedListOfLists target = new TargetWithNestedListOfLists();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
RelaxedDataBinder binder = new RelaxedDataBinder(target);
|
|
||||||
binder.normalizePath(wrapper, "nested[0][1]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[0][1]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBeanWrapperCreatesNewNestedMaps() throws Exception {
|
|
||||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
// For a nested map, you only have to get an element of it for it to be created
|
|
||||||
wrapper.getPropertyValue("nested[foo]");
|
|
||||||
// To decide what type to create for nested[foo] we need to look ahead and see
|
|
||||||
// what the user is trying to bind it to, e.g. if nested[foo][bar] then it's a map
|
|
||||||
wrapper.setPropertyValue("nested[foo]", new LinkedHashMap<String, Object>());
|
|
||||||
// But it might equally well be a collection, if nested[foo][0]
|
|
||||||
wrapper.setPropertyValue("nested[foo]", new ArrayList<>());
|
|
||||||
// Then it would have to be actually bound to get the list to auto-grow
|
|
||||||
wrapper.setPropertyValue("nested[foo][0]", "bar");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo][0]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBeanWrapperCreatesNewObjects() throws Exception {
|
|
||||||
TargetWithNestedObject target = new TargetWithNestedObject();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
// For a nested object, you have to set a property for it to be created
|
|
||||||
wrapper.setPropertyValue("nested.foo", "bar");
|
|
||||||
wrapper.getPropertyValue("nested");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBeanWrapperLists() throws Exception {
|
|
||||||
TargetWithNestedMapOfListOfString target = new TargetWithNestedMapOfListOfString();
|
|
||||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
|
|
||||||
wrapper.setAutoGrowNestedPaths(true);
|
|
||||||
TypeDescriptor descriptor = wrapper.getPropertyTypeDescriptor("nested");
|
|
||||||
assertThat(descriptor.isMap()).isTrue();
|
|
||||||
wrapper.getPropertyValue("nested[foo]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested")).isNotNull();
|
|
||||||
// You also need to bind to a value here
|
|
||||||
wrapper.setPropertyValue("nested[foo][0]", "bar");
|
|
||||||
wrapper.getPropertyValue("nested[foo][0]");
|
|
||||||
assertThat(wrapper.getPropertyValue("nested[foo]")).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Ignore("Work in progress")
|
|
||||||
public void testExpressionLists() throws Exception {
|
|
||||||
TargetWithNestedMapOfListOfString target = new TargetWithNestedMapOfListOfString();
|
|
||||||
LinkedHashMap<String, List<String>> map = new LinkedHashMap<>();
|
|
||||||
// map.put("foo", Arrays.asList("bar"));
|
|
||||||
target.setNested(map);
|
|
||||||
SpelExpressionParser parser = new SpelExpressionParser();
|
|
||||||
StandardEvaluationContext context = new StandardEvaluationContext(target);
|
|
||||||
context.addPropertyAccessor(new MapAccessor());
|
|
||||||
Expression expression = parser.parseExpression("nested.foo");
|
|
||||||
assertThat(expression.getValue(context)).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TargetWithNestedMap {
|
|
||||||
|
|
||||||
private Map<String, Object> nested;
|
|
||||||
|
|
||||||
public Map<String, Object> getNested() {
|
|
||||||
return this.nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNested(Map<String, Object> nested) {
|
|
||||||
this.nested = nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TargetWithNestedMapOfListOfString {
|
|
||||||
|
|
||||||
private Map<String, List<String>> nested;
|
|
||||||
|
|
||||||
public Map<String, List<String>> getNested() {
|
|
||||||
return this.nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNested(Map<String, List<String>> nested) {
|
|
||||||
this.nested = nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TargetWithNestedListOfMaps {
|
|
||||||
|
|
||||||
private List<Map<String, String>> nested;
|
|
||||||
|
|
||||||
public List<Map<String, String>> getNested() {
|
|
||||||
return this.nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNested(List<Map<String, String>> nested) {
|
|
||||||
this.nested = nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TargetWithNestedListOfLists {
|
|
||||||
|
|
||||||
private List<List<String>> nested;
|
|
||||||
|
|
||||||
public List<List<String>> getNested() {
|
|
||||||
return this.nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNested(List<List<String>> nested) {
|
|
||||||
this.nested = nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TargetWithNestedListOfBeansWithList {
|
|
||||||
|
|
||||||
private List<TargetWithList> nested;
|
|
||||||
|
|
||||||
public List<TargetWithList> getNested() {
|
|
||||||
return this.nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNested(List<TargetWithList> nested) {
|
|
||||||
this.nested = nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TargetWithList {
|
|
||||||
|
|
||||||
private List<VanillaTarget> list;
|
|
||||||
|
|
||||||
public List<VanillaTarget> getList() {
|
|
||||||
return this.list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setList(List<VanillaTarget> list) {
|
|
||||||
this.list = list;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TargetWithNestedMapOfBean {
|
|
||||||
|
|
||||||
private Map<String, VanillaTarget> nested;
|
|
||||||
|
|
||||||
public Map<String, VanillaTarget> getNested() {
|
|
||||||
return this.nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNested(Map<String, VanillaTarget> nested) {
|
|
||||||
this.nested = nested;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class VanillaTarget {
|
|
||||||
|
|
||||||
private String foo;
|
|
||||||
|
|
||||||
public String getFoo() {
|
|
||||||
return this.foo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFoo(String foo) {
|
|
||||||
this.foo = foo;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,156 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.bind;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.bind.ConverterBindingTests.TestConfig;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
|
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.core.convert.converter.GenericConverter;
|
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
|
||||||
import org.springframework.test.context.TestPropertySource;
|
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link ConfigurationProperties} binding with custom converters.
|
|
||||||
*
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
*/
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@DirtiesContext
|
|
||||||
@ContextConfiguration(classes = TestConfig.class, loader = SpringApplicationBindContextLoader.class)
|
|
||||||
@TestPropertySource(properties = { "foo=one", "bar=two" })
|
|
||||||
public class ConverterBindingTests {
|
|
||||||
|
|
||||||
@Value("${foo:}")
|
|
||||||
private String foo;
|
|
||||||
|
|
||||||
@Value("${bar:}")
|
|
||||||
private String bar;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private Wrapper properties;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void overridingOfPropertiesOrderOfAtPropertySources() {
|
|
||||||
assertThat(this.properties.getFoo().name).isEqualTo(this.foo);
|
|
||||||
assertThat(this.properties.getBar().name).isEqualTo(this.bar);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@EnableConfigurationProperties(Wrapper.class)
|
|
||||||
public static class TestConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConfigurationPropertiesBinding
|
|
||||||
public Converter<String, Foo> converter() {
|
|
||||||
return new Converter<String, ConverterBindingTests.Foo>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Foo convert(String source) {
|
|
||||||
return new Foo(source);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public GenericConverter genericConverter() {
|
|
||||||
return new GenericConverter() {
|
|
||||||
@Override
|
|
||||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
|
||||||
return Collections
|
|
||||||
.singleton(new ConvertiblePair(String.class, Bar.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object convert(Object source, TypeDescriptor sourceType,
|
|
||||||
TypeDescriptor targetType) {
|
|
||||||
return new Bar((String) source);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
|
||||||
return new PropertySourcesPlaceholderConfigurer();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Foo {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
public Foo(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Bar {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
public Bar(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@ConfigurationProperties
|
|
||||||
public static class Wrapper {
|
|
||||||
|
|
||||||
private Foo foo;
|
|
||||||
|
|
||||||
private Bar bar;
|
|
||||||
|
|
||||||
public Foo getFoo() {
|
|
||||||
return this.foo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFoo(Foo foo) {
|
|
||||||
this.foo = foo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bar getBar() {
|
|
||||||
return this.bar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBar(Bar bar) {
|
|
||||||
this.bar = bar;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.Arrays;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.WebApplicationType;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
|
||||||
import org.springframework.core.env.MapPropertySource;
|
|
||||||
import org.springframework.core.env.StandardEnvironment;
|
|
||||||
import org.springframework.test.context.ContextLoader;
|
|
||||||
import org.springframework.test.context.MergedContextConfiguration;
|
|
||||||
import org.springframework.test.context.support.AbstractContextLoader;
|
|
||||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link ContextLoader} used with binding tests.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
class SpringApplicationBindContextLoader extends AbstractContextLoader {
|
|
||||||
|
|
||||||
private static final String[] NO_SUFFIXES = new String[] {};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApplicationContext loadContext(MergedContextConfiguration config)
|
|
||||||
throws Exception {
|
|
||||||
SpringApplication application = new SpringApplication();
|
|
||||||
application.setMainApplicationClass(config.getTestClass());
|
|
||||||
application.setWebApplicationType(WebApplicationType.NONE);
|
|
||||||
application.setSources(
|
|
||||||
new LinkedHashSet<Object>(Arrays.asList(config.getClasses())));
|
|
||||||
ConfigurableEnvironment environment = new StandardEnvironment();
|
|
||||||
Map<String, Object> properties = new LinkedHashMap<>();
|
|
||||||
properties.put("spring.jmx.enabled", "false");
|
|
||||||
properties.putAll(TestPropertySourceUtils
|
|
||||||
.convertInlinedPropertiesToMap(config.getPropertySourceProperties()));
|
|
||||||
environment.getPropertySources().addAfter(
|
|
||||||
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
|
|
||||||
new MapPropertySource("integrationTest", properties));
|
|
||||||
application.setEnvironment(environment);
|
|
||||||
return application.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApplicationContext loadContext(String... locations) throws Exception {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getResourceSuffixes() {
|
|
||||||
return NO_SUFFIXES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getResourceSuffix() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2017 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.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.validation.Validation;
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.yaml.snakeyaml.error.YAMLException;
|
|
||||||
|
|
||||||
import org.springframework.context.support.StaticMessageSource;
|
|
||||||
import org.springframework.validation.BindException;
|
|
||||||
import org.springframework.validation.Validator;
|
|
||||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link YamlConfigurationFactory}
|
|
||||||
*
|
|
||||||
* @author Dave Syer
|
|
||||||
*/
|
|
||||||
public class YamlConfigurationFactoryTests {
|
|
||||||
|
|
||||||
private Validator validator;
|
|
||||||
|
|
||||||
private final Map<Class<?>, Map<String, String>> aliases = new HashMap<>();
|
|
||||||
|
|
||||||
private Foo createFoo(final String yaml) throws Exception {
|
|
||||||
YamlConfigurationFactory<Foo> factory = new YamlConfigurationFactory<>(Foo.class);
|
|
||||||
factory.setYaml(yaml);
|
|
||||||
factory.setPropertyAliases(this.aliases);
|
|
||||||
factory.setValidator(this.validator);
|
|
||||||
factory.setMessageSource(new StaticMessageSource());
|
|
||||||
factory.afterPropertiesSet();
|
|
||||||
return factory.getObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Jee createJee(final String yaml) throws Exception {
|
|
||||||
YamlConfigurationFactory<Jee> factory = new YamlConfigurationFactory<>(Jee.class);
|
|
||||||
factory.setYaml(yaml);
|
|
||||||
factory.setPropertyAliases(this.aliases);
|
|
||||||
factory.setValidator(this.validator);
|
|
||||||
factory.setMessageSource(new StaticMessageSource());
|
|
||||||
factory.afterPropertiesSet();
|
|
||||||
return factory.getObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidYamlLoadsWithNoErrors() throws Exception {
|
|
||||||
Foo foo = createFoo("name: blah\nbar: blah");
|
|
||||||
assertThat(foo.bar).isEqualTo("blah");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidYamlWithAliases() throws Exception {
|
|
||||||
this.aliases.put(Foo.class, Collections.singletonMap("foo-name", "name"));
|
|
||||||
Foo foo = createFoo("foo-name: blah\nbar: blah");
|
|
||||||
assertThat(foo.name).isEqualTo("blah");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = YAMLException.class)
|
|
||||||
public void unknownPropertyCausesLoadFailure() throws Exception {
|
|
||||||
createFoo("hi: hello\nname: foo\nbar: blah");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BindException.class)
|
|
||||||
public void missingPropertyCausesValidationError() throws Exception {
|
|
||||||
this.validator = new SpringValidatorAdapter(
|
|
||||||
Validation.buildDefaultValidatorFactory().getValidator());
|
|
||||||
createFoo("bar: blah");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWithPeriodInKey() throws Exception {
|
|
||||||
Jee jee = createJee("mymap:\n ? key1.key2\n : value");
|
|
||||||
assertThat(jee.mymap.get("key1.key2")).isEqualTo("value");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Foo {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
public String bar;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Jee {
|
|
||||||
|
|
||||||
public Map<Object, Object> mymap;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue