binding result tracking
This commit is contained in:
@@ -21,8 +21,10 @@ import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -72,7 +74,7 @@ public class Binder<T> {
|
||||
private boolean strict = false;
|
||||
|
||||
private static Formatter defaultFormatter = new Formatter() {
|
||||
|
||||
|
||||
public String format(Object object, Locale locale) {
|
||||
if (object == null) {
|
||||
return "";
|
||||
@@ -81,16 +83,15 @@ public class Binder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public Object parse(String formatted, Locale locale)
|
||||
throws ParseException {
|
||||
public Object parse(String formatted, Locale locale) throws ParseException {
|
||||
if (formatted == "") {
|
||||
return null;
|
||||
} else {
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new binder for the model object.
|
||||
* @param model the model object containing properties this binder will bind to
|
||||
@@ -98,13 +99,12 @@ public class Binder<T> {
|
||||
public Binder(T model) {
|
||||
this.model = model;
|
||||
bindings = new HashMap<String, Binding>();
|
||||
int parserConfig =
|
||||
SpelExpressionParserConfiguration.CreateListsOnAttemptToIndexIntoNull |
|
||||
SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize;
|
||||
int parserConfig = SpelExpressionParserConfiguration.CreateListsOnAttemptToIndexIntoNull
|
||||
| SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize;
|
||||
expressionParser = new SpelExpressionParser(parserConfig);
|
||||
typeConverter = new DefaultTypeConverter();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures if this binder is <i>strict</i>; a strict binder requires all bindings to be registered explicitly using {@link #add(BindingConfiguration)}.
|
||||
* An <i>optimistic</i> binder will implicitly create bindings as required to support {@link #bind(Map)} operations.
|
||||
@@ -140,10 +140,10 @@ public class Binder<T> {
|
||||
if (propertyType.isAnnotation()) {
|
||||
annotationFormatters.put(propertyType, new SimpleAnnotationFormatterFactory(formatter));
|
||||
} else {
|
||||
typeFormatters.put(propertyType, formatter);
|
||||
typeFormatters.put(propertyType, formatter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a AnnotationFormatterFactory that will format values of properties annotated with a specific annotation.
|
||||
* @param factory the annotation formatter factory
|
||||
@@ -176,22 +176,22 @@ public class Binder<T> {
|
||||
|
||||
/**
|
||||
* Bind values in the map to the properties of the model object.
|
||||
* TODO return BindingResults with getSuccesses()/getErrors()/etc?
|
||||
* @param propertyValues the property values map
|
||||
*/
|
||||
public void bind(Map<String, ? extends Object> propertyValues) {
|
||||
for (Map.Entry<String, ? extends Object> entry : propertyValues
|
||||
.entrySet()) {
|
||||
Binding binding = getBinding(entry.getKey());
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof String) {
|
||||
binding.setValue((String)entry.getValue());
|
||||
}
|
||||
else if (value instanceof String[]) {
|
||||
binding.setValues((String[])value);
|
||||
public List<BindingResult> bind(List<UserValue> userValues) {
|
||||
List<BindingResult> results = new ArrayList<BindingResult>(userValues.size());
|
||||
for (UserValue value : userValues) {
|
||||
Binding binding = getBinding(value.getProperty());
|
||||
if (value.isString()) {
|
||||
results.add(binding.setValue((String) value.getValue()));
|
||||
} else if (value.isStringArray()) {
|
||||
results.add(binding.setValues((String[]) value.getValue()));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Illegal argument " + value);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
class BindingImpl implements Binding {
|
||||
@@ -200,34 +200,60 @@ public class Binder<T> {
|
||||
|
||||
private Formatter formatter;
|
||||
|
||||
public BindingImpl(BindingConfiguration config)
|
||||
throws org.springframework.expression.ParseException {
|
||||
public BindingImpl(BindingConfiguration config) throws org.springframework.expression.ParseException {
|
||||
property = expressionParser.parseExpression(config.getProperty());
|
||||
formatter = config.getFormatter();
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
Object value;
|
||||
try {
|
||||
return format(property.getValue(createEvaluationContext()));
|
||||
value = property.getValue(createEvaluationContext());
|
||||
} catch (ExpressionException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
throw new IllegalStateException("Failed to get property expression value - this should not happen", e);
|
||||
}
|
||||
return format(value);
|
||||
}
|
||||
|
||||
public void setValue(String formatted) {
|
||||
setValue(parse(formatted, getFormatter()));
|
||||
public BindingResult setValue(String formatted) {
|
||||
Formatter formatter;
|
||||
try {
|
||||
formatter = getFormatter();
|
||||
} catch (EvaluationException e) {
|
||||
// could occur the property was not found or is not readable
|
||||
// TODO probably should not handle all EL failures, only type conversion & property not found?
|
||||
return new ExpressionEvaluationErrorResult(property.getExpressionString(), formatted, e);
|
||||
}
|
||||
Object parsed;
|
||||
try {
|
||||
parsed = formatter.parse(formatted, LocaleContextHolder.getLocale());
|
||||
} catch (ParseException e) {
|
||||
return new InvalidFormatResult(property.getExpressionString(), formatted, e);
|
||||
}
|
||||
return setValue(parsed, formatted);
|
||||
}
|
||||
|
||||
public String format(Object selectableValue) {
|
||||
Formatter formatter = getFormatter();
|
||||
Formatter formatter;
|
||||
try {
|
||||
formatter = getFormatter();
|
||||
} catch (EvaluationException e) {
|
||||
throw new IllegalStateException("Failed to get property expression value type - this should not happen", e);
|
||||
}
|
||||
Class<?> formattedType = getFormattedObjectType(formatter);
|
||||
selectableValue = typeConverter.convert(selectableValue, formattedType);
|
||||
return formatter.format(selectableValue, LocaleContextHolder.getLocale());
|
||||
}
|
||||
|
||||
public boolean isCollection() {
|
||||
TypeDescriptor<?> type = TypeDescriptor.valueOf(getValueType());
|
||||
return type.isCollection() || type.isArray();
|
||||
Class type;
|
||||
try {
|
||||
type = getValueType();
|
||||
} catch (EvaluationException e) {
|
||||
throw new IllegalArgumentException("Failed to get property expression value type - this should not happen", e);
|
||||
}
|
||||
TypeDescriptor<?> typeDesc = TypeDescriptor.valueOf(type);
|
||||
return typeDesc.isCollection() || typeDesc.isArray();
|
||||
}
|
||||
|
||||
public String[] getValues() {
|
||||
@@ -235,7 +261,7 @@ public class Binder<T> {
|
||||
try {
|
||||
multiValue = property.getValue(createEvaluationContext());
|
||||
} catch (EvaluationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
throw new IllegalStateException("Failed to get property expression value - this should not happen", e);
|
||||
}
|
||||
if (multiValue == null) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
@@ -243,42 +269,47 @@ public class Binder<T> {
|
||||
TypeDescriptor<?> type = TypeDescriptor.valueOf(multiValue.getClass());
|
||||
String[] formattedValues;
|
||||
if (type.isCollection()) {
|
||||
Collection<?> values = ((Collection<?>)multiValue);
|
||||
Collection<?> values = ((Collection<?>) multiValue);
|
||||
formattedValues = (String[]) Array.newInstance(String.class, values.size());
|
||||
copy(values, formattedValues);
|
||||
} else if (type.isArray()) {
|
||||
formattedValues = (String[]) Array.newInstance(String.class, Array.getLength(multiValue));
|
||||
copy((Iterable<?>) multiValue, formattedValues);
|
||||
copy((Iterable<?>) multiValue, formattedValues);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return formattedValues;
|
||||
}
|
||||
|
||||
public void setValues(String[] formattedValues) {
|
||||
Formatter formatter = getFormatter();
|
||||
public BindingResult setValues(String[] formatted) {
|
||||
Formatter formatter;
|
||||
try {
|
||||
formatter = getFormatter();
|
||||
} catch (EvaluationException e) {
|
||||
// could occur the property was not found or is not readable
|
||||
// TODO probably should not handle all EL failures, only type conversion & property not found?
|
||||
return new ExpressionEvaluationErrorResult(property.getExpressionString(), formatted, e);
|
||||
}
|
||||
Class parsedType = getFormattedObjectType(formatter);
|
||||
if (parsedType == null) {
|
||||
parsedType = String.class;
|
||||
}
|
||||
Object values = Array.newInstance(parsedType, formattedValues.length);
|
||||
for (int i = 0; i < formattedValues.length; i++) {
|
||||
Array.set(values, i, parse(formattedValues[i], formatter));
|
||||
Object parsed = Array.newInstance(parsedType, formatted.length);
|
||||
for (int i = 0; i < formatted.length; i++) {
|
||||
Object parsedValue;
|
||||
try {
|
||||
parsedValue = formatter.parse(formatted[i], LocaleContextHolder.getLocale());
|
||||
} catch (ParseException e) {
|
||||
return new InvalidFormatResult(property.getExpressionString(), formatted, e);
|
||||
}
|
||||
Array.set(parsed, i, parsedValue);
|
||||
}
|
||||
setValue(values);
|
||||
return setValue(parsed, formatted);
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private Object parse(String formatted, Formatter formatter) {
|
||||
try {
|
||||
return formatter.parse(formatted, LocaleContextHolder.getLocale());
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Invalid format " + formatted, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Formatter getFormatter() {
|
||||
private Formatter getFormatter() throws EvaluationException {
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
} else {
|
||||
@@ -299,21 +330,12 @@ public class Binder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getValueType() {
|
||||
try {
|
||||
return property.getValueType(createEvaluationContext());
|
||||
} catch (EvaluationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
private Class<?> getValueType() throws EvaluationException {
|
||||
return property.getValueType(createEvaluationContext());
|
||||
}
|
||||
|
||||
private Annotation[] getAnnotations() {
|
||||
try {
|
||||
return property.getValueTypeDescriptor(
|
||||
createEvaluationContext()).getAnnotations();
|
||||
} catch (EvaluationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
private Annotation[] getAnnotations() throws EvaluationException {
|
||||
return property.getValueTypeDescriptor(createEvaluationContext()).getAnnotations();
|
||||
}
|
||||
|
||||
private void copy(Iterable<?> values, String[] formattedValues) {
|
||||
@@ -323,15 +345,16 @@ public class Binder<T> {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void setValue(Object values) {
|
||||
|
||||
private BindingResult setValue(Object parsed, Object formatted) {
|
||||
try {
|
||||
property.setValue(createEvaluationContext(), values);
|
||||
} catch (ExpressionException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
property.setValue(createEvaluationContext(), parsed);
|
||||
return new SuccessResult(property.getExpressionString(), formatted);
|
||||
} catch (EvaluationException e) {
|
||||
return new ExpressionEvaluationErrorResult(property.getExpressionString(), formatted, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext() {
|
||||
@@ -341,7 +364,7 @@ public class Binder<T> {
|
||||
context.setTypeConverter(new StandardTypeConverter(typeConverter));
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
private Class getAnnotationType(AnnotationFormatterFactory factory) {
|
||||
Class classToIntrospect = factory.getClass();
|
||||
while (classToIntrospect != null) {
|
||||
@@ -356,10 +379,11 @@ public class Binder<T> {
|
||||
}
|
||||
classToIntrospect = classToIntrospect.getSuperclass();
|
||||
}
|
||||
throw new IllegalArgumentException("Unable to extract Annotation type A argument from AnnotationFormatterFactory ["
|
||||
+ factory.getClass().getName() + "]; does the factory parameterize the <A> generic type?");
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to extract Annotation type A argument from AnnotationFormatterFactory ["
|
||||
+ factory.getClass().getName() + "]; does the factory parameterize the <A> generic type?");
|
||||
}
|
||||
|
||||
|
||||
private Class getFormattedObjectType(Formatter formatter) {
|
||||
// TODO consider caching this info
|
||||
Class classToIntrospect = formatter.getClass();
|
||||
@@ -377,7 +401,7 @@ public class Binder<T> {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Class getParameterClass(Type parameterType, Class converterClass) {
|
||||
if (parameterType instanceof TypeVariable) {
|
||||
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass);
|
||||
@@ -392,7 +416,7 @@ public class Binder<T> {
|
||||
static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory {
|
||||
|
||||
private Formatter formatter;
|
||||
|
||||
|
||||
public SimpleAnnotationFormatterFactory(Formatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
@@ -400,6 +424,115 @@ public class Binder<T> {
|
||||
public Formatter getFormatter(Annotation annotation) {
|
||||
return formatter;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static class InvalidFormatResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
private Object formatted;
|
||||
|
||||
private ParseException e;
|
||||
|
||||
public InvalidFormatResult(String property, Object formatted, ParseException e) {
|
||||
this.property = property;
|
||||
this.formatted = formatted;
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
return "invalidFormat";
|
||||
}
|
||||
|
||||
public Throwable getErrorCause() {
|
||||
return e;
|
||||
}
|
||||
|
||||
public Object getUserValue() {
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
|
||||
static class ExpressionEvaluationErrorResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
private Object formatted;
|
||||
|
||||
private EvaluationException e;
|
||||
|
||||
public ExpressionEvaluationErrorResult(String property, Object formatted, EvaluationException e) {
|
||||
this.property = property;
|
||||
this.formatted = formatted;
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
if (e.getMessage().startsWith("EL1034E")) {
|
||||
return "typeConversionFailure";
|
||||
} else if (e.getMessage().startsWith("EL1008E")) {
|
||||
return "propertyNotFound";
|
||||
} else {
|
||||
// TODO return more specific code based on underlying EvaluationException error code
|
||||
return "couldNotSetValue";
|
||||
}
|
||||
}
|
||||
|
||||
public Throwable getErrorCause() {
|
||||
return e;
|
||||
}
|
||||
|
||||
public Object getUserValue() {
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
|
||||
static class SuccessResult implements BindingResult {
|
||||
|
||||
private String property;
|
||||
|
||||
private Object formatted;
|
||||
|
||||
public SuccessResult(String property, Object formatted) {
|
||||
this.property = property;
|
||||
this.formatted = formatted;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Throwable getErrorCause() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getUserValue() {
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ui.binding;
|
||||
|
||||
|
||||
/**
|
||||
* A binding between a user interface element and a model property.
|
||||
* @author Keith Donald
|
||||
@@ -32,7 +33,7 @@ public interface Binding {
|
||||
* Sets the model property value a from user-entered value.
|
||||
* @param formatted the value entered by the user
|
||||
*/
|
||||
void setValue(String formatted);
|
||||
BindingResult setValue(String formatted);
|
||||
|
||||
/**
|
||||
* Formats a candidate model property value for display in the user interface.
|
||||
@@ -59,6 +60,6 @@ public interface Binding {
|
||||
* When a collection binding, sets the model property values a from user-entered/selected values.
|
||||
* @param formattedValues the values entered by the user
|
||||
*/
|
||||
void setValues(String[] formattedValues);
|
||||
BindingResult setValues(String[] formatted);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2004-2009 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.ui.binding;
|
||||
|
||||
/**
|
||||
* A data binding result.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public interface BindingResult {
|
||||
|
||||
/**
|
||||
* The name of the model property associated with this binding result.
|
||||
*/
|
||||
String getProperty();
|
||||
|
||||
/**
|
||||
* Indicates if this result is an error result.
|
||||
*/
|
||||
boolean isError();
|
||||
|
||||
/**
|
||||
* If an error result, the error code; for example, "invalidFormat", "propertyNotFound", or "evaluationException".
|
||||
*/
|
||||
String getErrorCode();
|
||||
|
||||
/**
|
||||
* If an error result, the cause of the error.
|
||||
* @return the cause, or <code>null</code> if this is not an error
|
||||
*/
|
||||
Throwable getErrorCause();
|
||||
|
||||
/**
|
||||
* The raw user-entered value for which binding was attempted.
|
||||
* If not an error result, this value was successfully bound to the model.
|
||||
*/
|
||||
Object getUserValue();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2004-2009 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.ui.binding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds a user-entered value to bind to a model property.
|
||||
* @author Keith Donald
|
||||
* @see Binder#bind(List).
|
||||
*/
|
||||
public class UserValue {
|
||||
|
||||
private String property;
|
||||
|
||||
private Object value;
|
||||
|
||||
/**
|
||||
* Create a new user value
|
||||
* @param property the property associated with the value
|
||||
* @param value the actual user-entered value
|
||||
*/
|
||||
public UserValue(String property, Object value) {
|
||||
this.property = property;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The property the user-entered value should bind to.
|
||||
*/
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual user-entered value.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the user-entered value a String?
|
||||
*/
|
||||
public boolean isString() {
|
||||
return value instanceof String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the user-entered value a String[]?
|
||||
*/
|
||||
public boolean isStringArray() {
|
||||
return value instanceof String[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UserValue list with a single element.
|
||||
* @param property the property
|
||||
* @param value the actual user-entered value
|
||||
* @return the singleton user value list
|
||||
*/
|
||||
public static List<UserValue> singleton(String property, Object value) {
|
||||
List<UserValue> values = new ArrayList<UserValue>(1);
|
||||
values.add(new UserValue(property, value));
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,11 +7,10 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
@@ -19,6 +18,7 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.ui.format.date.DateFormatter;
|
||||
import org.springframework.ui.format.number.CurrencyAnnotationFormatterFactory;
|
||||
import org.springframework.ui.format.number.CurrencyFormat;
|
||||
@@ -40,54 +40,67 @@ public class BinderTests {
|
||||
@Test
|
||||
public void bindSingleValuesWithDefaultTypeConverterConversion() {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
propertyValues.put("string", "test");
|
||||
propertyValues.put("integer", "3");
|
||||
propertyValues.put("foo", "BAR");
|
||||
binder.bind(propertyValues);
|
||||
List<UserValue> values = new ArrayList<UserValue>();
|
||||
values.add(new UserValue("string", "test"));
|
||||
values.add(new UserValue("integer", "3"));
|
||||
values.add(new UserValue("foo", "BAR"));
|
||||
List<BindingResult> results = binder.bind(values);
|
||||
assertEquals(3, results.size());
|
||||
|
||||
assertEquals("string", results.get(0).getProperty());
|
||||
assertFalse(results.get(0).isError());
|
||||
assertNull(results.get(0).getErrorCause());
|
||||
assertEquals("test", results.get(0).getUserValue());
|
||||
|
||||
assertEquals("integer", results.get(1).getProperty());
|
||||
assertFalse(results.get(1).isError());
|
||||
assertNull(results.get(1).getErrorCause());
|
||||
assertEquals("3", results.get(1).getUserValue());
|
||||
|
||||
assertEquals("foo", results.get(2).getProperty());
|
||||
assertFalse(results.get(2).isError());
|
||||
assertNull(results.get(2).getErrorCause());
|
||||
assertEquals("BAR", results.get(2).getUserValue());
|
||||
|
||||
assertEquals("test", binder.getModel().getString());
|
||||
assertEquals(3, binder.getModel().getInteger());
|
||||
assertEquals(FooEnum.BAR, binder.getModel().getFoo());
|
||||
}
|
||||
|
||||
// TODO should update error context, not throw exception
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
@Test
|
||||
public void bindSingleValuesWithDefaultTypeCoversionFailure() {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
propertyValues.put("string", "test");
|
||||
propertyValues.put("integer", "bogus");
|
||||
propertyValues.put("foo", "bogus");
|
||||
binder.bind(propertyValues);
|
||||
List<UserValue> values = new ArrayList<UserValue>();
|
||||
values.add(new UserValue("string", "test"));
|
||||
// bad value
|
||||
values.add(new UserValue("integer", "bogus"));
|
||||
values.add(new UserValue("foo", "BAR"));
|
||||
List<BindingResult> results = binder.bind(values);
|
||||
assertEquals(3, results.size());
|
||||
assertTrue(results.get(1).isError());
|
||||
assertEquals("typeConversionFailure", results.get(1).getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindSingleValuePropertyFormatter() throws ParseException {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
binder.add(new BindingConfiguration("date", new DateFormatter()));
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
propertyValues.put("date", "2009-06-01");
|
||||
binder.bind(propertyValues);
|
||||
binder.bind(UserValue.singleton("date", "2009-06-01"));
|
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), binder.getModel().getDate());
|
||||
}
|
||||
|
||||
// TODO should update error context, not throw exception
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
@Test
|
||||
public void bindSingleValuePropertyFormatterParseException() {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
binder.add(new BindingConfiguration("date", new DateFormatter()));
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
propertyValues.put("date", "bogus");
|
||||
binder.bind(propertyValues);
|
||||
binder.bind(UserValue.singleton("date", "bogus"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindSingleValueWithFormatterRegistedByType() throws ParseException {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
binder.add(new DateFormatter(), Date.class);
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
propertyValues.put("date", "2009-06-01");
|
||||
binder.bind(propertyValues);
|
||||
binder.bind(UserValue.singleton("date", "2009-06-01"));
|
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), binder.getModel().getDate());
|
||||
}
|
||||
|
||||
@@ -95,9 +108,7 @@ public class BinderTests {
|
||||
public void bindSingleValueWithFormatterRegisteredByAnnotation() throws ParseException {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
binder.add(new CurrencyFormatter(), CurrencyFormat.class);
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
propertyValues.put("currency", "$23.56");
|
||||
binder.bind(propertyValues);
|
||||
binder.bind(UserValue.singleton("currency", "$23.56"));
|
||||
assertEquals(new BigDecimal("23.56"), binder.getModel().getCurrency());
|
||||
}
|
||||
|
||||
@@ -105,20 +116,28 @@ public class BinderTests {
|
||||
public void bindSingleValueWithnAnnotationFormatterFactoryRegistered() throws ParseException {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
binder.add(new CurrencyAnnotationFormatterFactory());
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
propertyValues.put("currency", "$23.56");
|
||||
binder.bind(propertyValues);
|
||||
binder.bind(UserValue.singleton("currency", "$23.56"));
|
||||
assertEquals(new BigDecimal("23.56"), binder.getModel().getCurrency());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindSingleValuePropertyNotFound() throws ParseException {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
List<BindingResult> results = binder.bind(UserValue.singleton("bogus", "2009-06-01"));
|
||||
assertEquals(1, results.size());
|
||||
assertTrue(results.get(0).isError());
|
||||
assertEquals("propertyNotFound", results.get(0).getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBindingOptimistic() {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
Binding b = binder.getBinding("integer");
|
||||
assertFalse(b.isCollection());
|
||||
assertEquals("0", b.getValue());
|
||||
b.setValue("5");
|
||||
BindingResult result = b.setValue("5");
|
||||
assertEquals("5", b.getValue());
|
||||
assertFalse(result.isError());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -131,8 +150,9 @@ public class BinderTests {
|
||||
b = binder.getBinding("integer");
|
||||
assertFalse(b.isCollection());
|
||||
assertEquals("0", b.getValue());
|
||||
b.setValue("5");
|
||||
BindingResult result = b.setValue("5");
|
||||
assertEquals("5", b.getValue());
|
||||
assertFalse(result.isError());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -172,21 +192,24 @@ public class BinderTests {
|
||||
assertEquals("BAZ", values[1]);
|
||||
assertEquals("BOOP", values[2]);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void getBindingMultiValuedTypeConversionError() {
|
||||
|
||||
@Test
|
||||
public void getBindingMultiValuedTypeConversionFailure() {
|
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean());
|
||||
Binding b = binder.getBinding("foos");
|
||||
assertTrue(b.isCollection());
|
||||
assertEquals(0, b.getValues().length);
|
||||
b.setValues(new String[] { "BAR", "BOGUS", "BOOP" });
|
||||
BindingResult result = b.setValues(new String[] { "BAR", "BOGUS", "BOOP" });
|
||||
assertTrue(result.isError());
|
||||
assertTrue(result.getErrorCause() instanceof EvaluationException);
|
||||
assertEquals("typeConversionFailure", result.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindHandleNullValueInNestedPath() {
|
||||
TestBean testbean = new TestBean();
|
||||
Binder<TestBean> binder = new Binder<TestBean>(testbean);
|
||||
Map<String, String> propertyValues = new HashMap<String, String>();
|
||||
List<UserValue> values = new ArrayList<UserValue>();
|
||||
|
||||
// EL configured with some options from SpelExpressionParserConfiguration:
|
||||
// (see where Binder creates the parser)
|
||||
@@ -195,27 +218,28 @@ public class BinderTests {
|
||||
// are new instances of the type of the list entry, they are not null.
|
||||
// not currently doing anything for maps or arrays
|
||||
|
||||
propertyValues.put("addresses[0].street", "4655 Macy Lane");
|
||||
propertyValues.put("addresses[0].city", "Melbourne");
|
||||
propertyValues.put("addresses[0].state", "FL");
|
||||
propertyValues.put("addresses[0].state", "35452");
|
||||
values.add(new UserValue("addresses[0].street", "4655 Macy Lane"));
|
||||
values.add(new UserValue("addresses[0].city", "Melbourne"));
|
||||
values.add(new UserValue("addresses[0].state", "FL"));
|
||||
values.add(new UserValue("addresses[0].state", "35452"));
|
||||
|
||||
// Auto adds new Address at 1
|
||||
propertyValues.put("addresses[1].street", "1234 Rostock Circle");
|
||||
propertyValues.put("addresses[1].city", "Palm Bay");
|
||||
propertyValues.put("addresses[1].state", "FL");
|
||||
propertyValues.put("addresses[1].state", "32901");
|
||||
values.add(new UserValue("addresses[1].street", "1234 Rostock Circle"));
|
||||
values.add(new UserValue("addresses[1].city", "Palm Bay"));
|
||||
values.add(new UserValue("addresses[1].state", "FL"));
|
||||
values.add(new UserValue("addresses[1].state", "32901"));
|
||||
|
||||
// Auto adds new Address at 5 (plus intermediates 2,3,4)
|
||||
propertyValues.put("addresses[5].street", "1234 Rostock Circle");
|
||||
propertyValues.put("addresses[5].city", "Palm Bay");
|
||||
propertyValues.put("addresses[5].state", "FL");
|
||||
propertyValues.put("addresses[5].state", "32901");
|
||||
values.add(new UserValue("addresses[5].street", "1234 Rostock Circle"));
|
||||
values.add(new UserValue("addresses[5].city", "Palm Bay"));
|
||||
values.add(new UserValue("addresses[5].state", "FL"));
|
||||
values.add(new UserValue("addresses[5].state", "32901"));
|
||||
|
||||
binder.bind(propertyValues);
|
||||
List<BindingResult> results = binder.bind(values);
|
||||
Assert.assertEquals(6,testbean.addresses.size());
|
||||
Assert.assertEquals("Palm Bay",testbean.addresses.get(1).city);
|
||||
Assert.assertNotNull(testbean.addresses.get(2));
|
||||
assertEquals(12, results.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user