added bind template / field binder

This commit is contained in:
Keith Donald 2009-07-26 20:23:51 +00:00
parent ccb0a30169
commit a66aa8c320
8 changed files with 218 additions and 128 deletions

View File

@ -22,15 +22,17 @@ import java.util.Map;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
* @see #bind(Map) * @see #bind(Map)
* @param <M> The type of model this binder binds to
*/ */
public interface Binder { public interface Binder<M> {
/** /**
* Bind submitted field values. * Bind submitted field values.
* @param fieldValues the field values to bind * @param fieldValues the field values to bind
* @param model the model to bind to
* @return the results of the binding operation * @return the results of the binding operation
* @throws MissingFieldException when the fieldValues Map is missing required fields * @throws MissingFieldException when the fieldValues Map is missing required fields
*/ */
BindingResults bind(Map<String, ? extends Object> fieldValues); BindingResults bind(Map<String, ? extends Object> fieldValues, M model);
} }

View File

@ -15,8 +15,6 @@
*/ */
package org.springframework.model.binder.support; package org.springframework.model.binder.support;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
@ -27,26 +25,32 @@ import org.springframework.model.binder.MissingFieldException;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* A base {@link Binder binder} implementation designed for subclassing. * Base Binder implementation that defines common structural elements.
* Subclasses should parameterized & implement {@link #bind(Map, Object)}.
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
* @see #setRequiredFields(String[]) * @see #setRequiredFields(String[])
* @see #setMessageSource(MessageSource) * @see #setMessageSource(MessageSource)
* @see #bind(Map) * @see #bind(Map, Object)
* @see #createBindTemplate()
*/ */
public abstract class AbstractBinder implements Binder { public abstract class AbstractBinder<M> implements Binder<M> {
private String[] requiredFields; private BindTemplate bindTemplate;
private MessageSource messageSource; private MessageSource messageSource;
public AbstractBinder() {
bindTemplate = createBindTemplate();
}
/** /**
* Configure the fields for which values must be present in each bind attempt. * Configure the fields for which values must be present in each bind attempt.
* @param fieldNames the required field names * @param fieldNames the required field names
* @see MissingFieldException * @see MissingFieldException
*/ */
public void setRequiredFields(String[] fieldNames) { public void setRequiredFields(String[] fieldNames) {
this.requiredFields = fieldNames; bindTemplate.setRequiredFields(fieldNames);
} }
/** /**
@ -58,31 +62,23 @@ public abstract class AbstractBinder implements Binder {
this.messageSource = messageSource; this.messageSource = messageSource;
} }
// implementing Binder public abstract BindingResults bind(Map<String, ? extends Object> fieldValues, M model);
public BindingResults bind(Map<String, ? extends Object> fieldValues) { // subclass hooks
fieldValues = filter(fieldValues);
checkRequired(fieldValues);
ArrayListBindingResults results = new ArrayListBindingResults(fieldValues.size());
for (Map.Entry<String, ? extends Object> fieldValue : fieldValues.entrySet()) {
results.add(bindField(fieldValue.getKey(), fieldValue.getValue()));
}
return results;
}
// subclassing hooks
/** /**
* Hook subclasses may use to filter the source values to bind. * Create the template defining the bulk-binding algorithm.
* This hook allows the binder to pre-process the field values before binding occurs. * Subclasses may override to customize the algorithm.
* For example, a Binder might insert empty or default values for fields that are not present.
* As another example, a Binder might collapse multiple source values into a single source value.
* Default implementation simply returns the fieldValues Map unchanged.
* @param fieldValues the original fieldValues Map provided by the caller
* @return the filtered fieldValues Map that will be used to bind
*/ */
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues) { protected BindTemplate createBindTemplate() {
return fieldValues; return new BindTemplate();
}
/**
* The template defining the bulk-binding algorithm.
*/
protected BindTemplate getBindTemplate() {
return bindTemplate;
} }
/** /**
@ -92,35 +88,4 @@ public abstract class AbstractBinder implements Binder {
return messageSource; return messageSource;
} }
/**
* Hook method subclasses override to perform a single field binding.
* @param name the field name
* @param value the field value
* @return the binding result
*/
protected abstract BindingResult bindField(String name, Object value);
// internal helpers
private void checkRequired(Map<String, ? extends Object> fieldValues) {
if (requiredFields == null) {
return;
}
List<String> missingRequired = new ArrayList<String>();
for (String required : requiredFields) {
boolean found = false;
for (String property : fieldValues.keySet()) {
if (property.equals(required)) {
found = true;
}
}
if (!found) {
missingRequired.add(required);
}
}
if (!missingRequired.isEmpty()) {
throw new MissingFieldException(missingRequired, fieldValues);
}
}
} }

View File

@ -0,0 +1,80 @@
/*
* 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.model.binder.support;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.model.binder.BindingResults;
import org.springframework.model.binder.MissingFieldException;
/**
* A template that encapsulates the general bulk-binding algorithm.
* @author Keith Donald
* @since 3.0
* @see #setRequiredFields(String[])
* @see #bind(Map)
*/
public class BindTemplate {
private String[] requiredFields;
/**
* Configure the fields for which values must be present in each bind attempt.
* @param fieldNames the required field names
* @see MissingFieldException
*/
public void setRequiredFields(String[] fieldNames) {
this.requiredFields = fieldNames;
}
// implementing Binder
public BindingResults bind(Map<String, ? extends Object> fieldValues, FieldBinder fieldBinder) {
checkRequired(fieldValues);
ArrayListBindingResults results = new ArrayListBindingResults(fieldValues.size());
for (Map.Entry<String, ? extends Object> fieldValue : fieldValues.entrySet()) {
results.add(fieldBinder.bind(fieldValue.getKey(), fieldValue.getValue()));
}
return results;
}
// internal helpers
private void checkRequired(Map<String, ? extends Object> fieldValues) {
if (requiredFields == null) {
return;
}
List<String> missingRequired = new ArrayList<String>();
for (String required : requiredFields) {
boolean found = false;
for (String property : fieldValues.keySet()) {
if (property.equals(required)) {
found = true;
}
}
if (!found) {
missingRequired.add(required);
}
}
if (!missingRequired.isEmpty()) {
throw new MissingFieldException(missingRequired, fieldValues);
}
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.model.binder.support;
import org.springframework.model.binder.BindingResult;
/**
* BindTemplate callback interface for binding a single field value.
* @author Keith Donald
* @see BindTemplate#bind(java.util.Map, BindCallback)
*/
public interface FieldBinder {
/**
* Bind a single field.
* @param fieldName the field name
* @param value the field value
* @return the binding result
*/
BindingResult bind(String fieldName, Object value);
}

View File

@ -18,62 +18,78 @@ package org.springframework.model.ui.support;
import java.util.Map; import java.util.Map;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.model.binder.Binder;
import org.springframework.model.binder.BindingResult; import org.springframework.model.binder.BindingResult;
import org.springframework.model.binder.BindingResults;
import org.springframework.model.binder.support.AbstractBinder; import org.springframework.model.binder.support.AbstractBinder;
import org.springframework.model.binder.support.AlertBindingResult; import org.springframework.model.binder.support.AlertBindingResult;
import org.springframework.model.binder.support.FieldBinder;
import org.springframework.model.binder.support.FieldNotEditableResult; import org.springframework.model.binder.support.FieldNotEditableResult;
import org.springframework.model.binder.support.FieldNotFoundResult; import org.springframework.model.binder.support.FieldNotFoundResult;
import org.springframework.model.ui.BindingStatus; import org.springframework.model.ui.BindingStatus;
import org.springframework.model.ui.FieldModel; import org.springframework.model.ui.FieldModel;
import org.springframework.model.ui.FieldNotFoundException; import org.springframework.model.ui.FieldNotFoundException;
import org.springframework.model.ui.PresentationModel; import org.springframework.model.ui.PresentationModel;
import org.springframework.util.Assert;
/** /**
* A generic {@link Binder binder} suitable for use in most environments. * Binds field values to PresentationModel objects.
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
* @see #setMessageSource(MessageSource) * @see #setMessageSource(MessageSource)
* @see #setRequiredFields(String[]) * @see #setRequiredFields(String[])
* @see #bind(Map) * @see #bind(Map, PresentationModel)
*/ */
public class PresentationModelBinder extends AbstractBinder { public class PresentationModelBinder extends AbstractBinder<PresentationModel> {
private PresentationModel presentationModel; public BindingResults bind(Map<String, ? extends Object> fieldValues, PresentationModel model) {
fieldValues = filter(fieldValues, model);
public PresentationModelBinder(PresentationModel presentationModel) { return getBindTemplate().bind(fieldValues, new FieldModelBinder(model, getMessageSource()));
Assert.notNull(presentationModel, "The PresentationModel is required");
this.presentationModel = presentationModel;
} }
// subclassing hooks // subclassing hooks
/** /**
* Get the model for the field. * Filter the fields to bind.
* @param fieldName * Allows for pre-processing the fieldValues Map before any binding occurs.
* @return the field model * For example, you might insert empty or default values for fields that are not present.
* @throws NoSuchFieldException if no such field exists * As another example, you might collapse multiple fields into a single field.
* Default implementation simply returns the fieldValues Map unchanged.
* @param fieldValues the original fieldValues Map provided by the caller
* @return the filtered fieldValues Map that will be used to bind
*/ */
protected FieldModel getFieldModel(String fieldName) { protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues, PresentationModel model) {
return presentationModel.getFieldModel(fieldName); return fieldValues;
} }
protected BindingResult bindField(String name, Object value) { // internal helpers
private static class FieldModelBinder implements FieldBinder {
private PresentationModel presentationModel;
private MessageSource messageSource;
public FieldModelBinder(PresentationModel presentationModel, MessageSource messageSource) {
this.presentationModel = presentationModel;
this.messageSource = messageSource;
}
public BindingResult bind(String fieldName, Object value) {
FieldModel field; FieldModel field;
try { try {
field = getFieldModel(name); field = presentationModel.getFieldModel(fieldName);
} catch (FieldNotFoundException e) { } catch (FieldNotFoundException e) {
return new FieldNotFoundResult(name, value, getMessageSource()); return new FieldNotFoundResult(fieldName, value, messageSource);
} }
if (!field.isEditable()) { if (!field.isEditable()) {
return new FieldNotEditableResult(name, value, getMessageSource()); return new FieldNotEditableResult(fieldName, value, messageSource);
} else { } else {
field.applySubmittedValue(value); field.applySubmittedValue(value);
if (field.getBindingStatus() == BindingStatus.DIRTY) { if (field.getBindingStatus() == BindingStatus.DIRTY) {
field.commit(); field.commit();
} }
return new AlertBindingResult(name, value, field.getStatusAlert()); return new AlertBindingResult(fieldName, value, field.getStatusAlert());
} }
} }
}
} }

View File

@ -28,6 +28,7 @@ import org.springframework.model.ui.PresentationModel;
* @since 3.0 * @since 3.0
* @see #setDefaultPrefix(String) * @see #setDefaultPrefix(String)
* @see #setPresentPrefix(String) * @see #setPresentPrefix(String)
* @see #filter(Map, PresentationModel)
*/ */
public class WebBinder extends PresentationModelBinder { public class WebBinder extends PresentationModelBinder {
@ -35,14 +36,6 @@ public class WebBinder extends PresentationModelBinder {
private String presentPrefix = "_"; private String presentPrefix = "_";
/**
* Creates a new web binder for the model object.
* @param model the model object containing properties this binder will bind to
*/
public WebBinder(PresentationModel bindingFactory) {
super(bindingFactory);
}
/** /**
* Configure the prefix used to detect the default value for a field when no value is submitted. * Configure the prefix used to detect the default value for a field when no value is submitted.
* Default is '!'. * Default is '!'.
@ -61,7 +54,7 @@ public class WebBinder extends PresentationModelBinder {
} }
@Override @Override
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues) { protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues, PresentationModel model) {
LinkedHashMap<String, Object> filteredValues = new LinkedHashMap<String, Object>(); LinkedHashMap<String, Object> filteredValues = new LinkedHashMap<String, Object>();
for (Map.Entry<String, ? extends Object> entry : fieldValues.entrySet()) { for (Map.Entry<String, ? extends Object> entry : fieldValues.entrySet()) {
String field = entry.getKey(); String field = entry.getKey();
@ -74,7 +67,7 @@ public class WebBinder extends PresentationModelBinder {
} else if (field.startsWith(presentPrefix)) { } else if (field.startsWith(presentPrefix)) {
field = field.substring(presentPrefix.length()); field = field.substring(presentPrefix.length());
if (!fieldValues.containsKey(field) && !fieldValues.containsKey(defaultPrefix + field)) { if (!fieldValues.containsKey(field) && !fieldValues.containsKey(defaultPrefix + field)) {
value = getEmptyValue(getFieldModel(field)); value = getEmptyValue(model.getFieldModel(field));
filteredValues.put(field, value); filteredValues.put(field, value);
} }
} else { } else {

View File

@ -47,7 +47,7 @@ public class PresentationModelBinderTests {
public void setUp() { public void setUp() {
bean = new TestBean(); bean = new TestBean();
presentationModel = new DefaultPresentationModel(bean); presentationModel = new DefaultPresentationModel(bean);
binder = new PresentationModelBinder(presentationModel); binder = new PresentationModelBinder();
LocaleContextHolder.setLocale(Locale.US); LocaleContextHolder.setLocale(Locale.US);
} }
@ -62,7 +62,7 @@ public class PresentationModelBinderTests {
values.put("string", "test"); values.put("string", "test");
values.put("integer", "3"); values.put("integer", "3");
values.put("foo", "BAR"); values.put("foo", "BAR");
BindingResults results = binder.bind(values); BindingResults results = binder.bind(values, presentationModel);
assertEquals(3, results.size()); assertEquals(3, results.size());
assertEquals("string", results.get(0).getFieldName()); assertEquals("string", results.get(0).getFieldName());
@ -89,7 +89,7 @@ public class PresentationModelBinderTests {
// bad value // bad value
values.put("integer", "bogus"); values.put("integer", "bogus");
values.put("foo", "BAR"); values.put("foo", "BAR");
BindingResults results = binder.bind(values); BindingResults results = binder.bind(values, presentationModel);
assertEquals(3, results.size()); assertEquals(3, results.size());
assertTrue(results.get(1).isFailure()); assertTrue(results.get(1).isFailure());
assertEquals("typeMismatch", results.get(1).getAlert().getCode()); assertEquals("typeMismatch", results.get(1).getAlert().getCode());
@ -98,14 +98,14 @@ public class PresentationModelBinderTests {
@Test @Test
public void bindSingleValuePropertyFormatter() throws ParseException { public void bindSingleValuePropertyFormatter() throws ParseException {
presentationModel.field("date").formatWith(new DateFormatter()); presentationModel.field("date").formatWith(new DateFormatter());
binder.bind(Collections.singletonMap("date", "2009-06-01")); binder.bind(Collections.singletonMap("date", "2009-06-01"), presentationModel);
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate()); assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
} }
@Test @Test
public void bindSingleValuePropertyFormatterParseException() { public void bindSingleValuePropertyFormatterParseException() {
presentationModel.field("date").formatWith(new DateFormatter()); presentationModel.field("date").formatWith(new DateFormatter());
BindingResults results = binder.bind(Collections.singletonMap("date", "bogus")); BindingResults results = binder.bind(Collections.singletonMap("date", "bogus"), presentationModel);
assertEquals(1, results.size()); assertEquals(1, results.size());
assertTrue(results.get(0).isFailure()); assertTrue(results.get(0).isFailure());
assertEquals("typeMismatch", results.get(0).getAlert().getCode()); assertEquals("typeMismatch", results.get(0).getAlert().getCode());
@ -117,7 +117,7 @@ public class PresentationModelBinderTests {
formatterRegistry.add(Date.class, new DateFormatter()); formatterRegistry.add(Date.class, new DateFormatter());
presentationModel.setFormatterRegistry(formatterRegistry); presentationModel.setFormatterRegistry(formatterRegistry);
binder.bind(Collections.singletonMap("date", "2009-06-01")); binder.bind(Collections.singletonMap("date", "2009-06-01"), presentationModel);
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate()); assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate());
} }
@ -127,13 +127,13 @@ public class PresentationModelBinderTests {
formatterRegistry.add(new CurrencyAnnotationFormatterFactory()); formatterRegistry.add(new CurrencyAnnotationFormatterFactory());
presentationModel.setFormatterRegistry(formatterRegistry); presentationModel.setFormatterRegistry(formatterRegistry);
binder.bind(Collections.singletonMap("currency", "$23.56")); binder.bind(Collections.singletonMap("currency", "$23.56"), presentationModel);
assertEquals(new BigDecimal("23.56"), bean.getCurrency()); assertEquals(new BigDecimal("23.56"), bean.getCurrency());
} }
@Test @Test
public void bindSingleValuePropertyNotFound() throws ParseException { public void bindSingleValuePropertyNotFound() throws ParseException {
BindingResults results = binder.bind(Collections.singletonMap("bogus", "2009-06-01")); BindingResults results = binder.bind(Collections.singletonMap("bogus", "2009-06-01"), presentationModel);
assertEquals("bogus", results.get(0).getFieldName()); assertEquals("bogus", results.get(0).getFieldName());
assertTrue(results.get(0).isFailure()); assertTrue(results.get(0).isFailure());
assertEquals("fieldNotFound", results.get(0).getAlert().getCode()); assertEquals("fieldNotFound", results.get(0).getAlert().getCode());
@ -143,7 +143,7 @@ public class PresentationModelBinderTests {
public void bindMissingRequiredSourceValue() { public void bindMissingRequiredSourceValue() {
binder.setRequiredFields(new String[] { "integer" }); binder.setRequiredFields(new String[] { "integer" });
// missing "integer" - violated bind contract // missing "integer" - violated bind contract
binder.bind(Collections.singletonMap("string", "test")); binder.bind(Collections.singletonMap("string", "test"), presentationModel);
} }
@Test @Test
@ -236,7 +236,7 @@ public class PresentationModelBinderTests {
Map<String, String[]> values = new LinkedHashMap<String, String[]>(); Map<String, String[]> values = new LinkedHashMap<String, String[]>();
values.put("addresses", new String[] { "4655 Macy Lane:Melbourne:FL:35452", values.put("addresses", new String[] { "4655 Macy Lane:Melbourne:FL:35452",
"1234 Rostock Circle:Palm Bay:FL:32901", "1977 Bel Aire Estates:Coker:AL:12345" }); "1234 Rostock Circle:Palm Bay:FL:32901", "1977 Bel Aire Estates:Coker:AL:12345" });
binder.bind(values); binder.bind(values, presentationModel);
assertEquals(3, bean.addresses.size()); assertEquals(3, bean.addresses.size());
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); assertEquals("4655 Macy Lane", bean.addresses.get(0).street);
assertEquals("Melbourne", bean.addresses.get(0).city); assertEquals("Melbourne", bean.addresses.get(0).city);
@ -250,7 +250,7 @@ public class PresentationModelBinderTests {
values.put("addresses[0]", "4655 Macy Lane:Melbourne:FL:35452"); values.put("addresses[0]", "4655 Macy Lane:Melbourne:FL:35452");
values.put("addresses[1]", "1234 Rostock Circle:Palm Bay:FL:32901"); values.put("addresses[1]", "1234 Rostock Circle:Palm Bay:FL:32901");
values.put("addresses[5]", "1977 Bel Aire Estates:Coker:AL:12345"); values.put("addresses[5]", "1977 Bel Aire Estates:Coker:AL:12345");
binder.bind(values); binder.bind(values, presentationModel);
Assert.assertEquals(6, bean.addresses.size()); Assert.assertEquals(6, bean.addresses.size());
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); assertEquals("4655 Macy Lane", bean.addresses.get(0).street);
assertEquals("Melbourne", bean.addresses.get(0).city); assertEquals("Melbourne", bean.addresses.get(0).city);
@ -267,7 +267,7 @@ public class PresentationModelBinderTests {
values values
.put("addresses", .put("addresses",
"4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345"); "4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345");
binder.bind(values); binder.bind(values, presentationModel);
Assert.assertEquals(3, bean.addresses.size()); Assert.assertEquals(3, bean.addresses.size());
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); assertEquals("4655 Macy Lane", bean.addresses.get(0).street);
assertEquals("Melbourne", bean.addresses.get(0).city); assertEquals("Melbourne", bean.addresses.get(0).city);
@ -289,7 +289,7 @@ public class PresentationModelBinderTests {
values values
.put("addresses", .put("addresses",
"4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345"); "4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345");
binder.bind(values); binder.bind(values, presentationModel);
Assert.assertEquals(3, bean.addresses.size()); Assert.assertEquals(3, bean.addresses.size());
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); assertEquals("4655 Macy Lane", bean.addresses.get(0).street);
assertEquals("Melbourne", bean.addresses.get(0).city); assertEquals("Melbourne", bean.addresses.get(0).city);
@ -371,7 +371,7 @@ public class PresentationModelBinderTests {
values.put("addresses[5].state", "FL"); values.put("addresses[5].state", "FL");
values.put("addresses[5].zip", "32901"); values.put("addresses[5].zip", "32901");
BindingResults results = binder.bind(values); BindingResults results = binder.bind(values, presentationModel);
Assert.assertEquals(6, bean.addresses.size()); Assert.assertEquals(6, bean.addresses.size());
Assert.assertEquals("Palm Bay", bean.addresses.get(1).city); Assert.assertEquals("Palm Bay", bean.addresses.get(1).city);
Assert.assertNotNull(bean.addresses.get(2)); Assert.assertNotNull(bean.addresses.get(2));
@ -382,7 +382,7 @@ public class PresentationModelBinderTests {
public void bindToMap() { public void bindToMap() {
Map<String, String[]> values = new LinkedHashMap<String, String[]>(); Map<String, String[]> values = new LinkedHashMap<String, String[]>();
values.put("favoriteFoodsByGroup", new String[] { "DAIRY=Milk", "FRUIT=Peaches", "MEAT=Ham" }); values.put("favoriteFoodsByGroup", new String[] { "DAIRY=Milk", "FRUIT=Peaches", "MEAT=Ham" });
binder.bind(values); binder.bind(values, presentationModel);
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size()); Assert.assertEquals(3, bean.favoriteFoodsByGroup.size());
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY)); assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY));
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT)); assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT));
@ -395,7 +395,7 @@ public class PresentationModelBinderTests {
values.put("favoriteFoodsByGroup[DAIRY]", "Milk"); values.put("favoriteFoodsByGroup[DAIRY]", "Milk");
values.put("favoriteFoodsByGroup[FRUIT]", "Peaches"); values.put("favoriteFoodsByGroup[FRUIT]", "Peaches");
values.put("favoriteFoodsByGroup[MEAT]", "Ham"); values.put("favoriteFoodsByGroup[MEAT]", "Ham");
binder.bind(values); binder.bind(values, presentationModel);
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size()); Assert.assertEquals(3, bean.favoriteFoodsByGroup.size());
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY)); assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY));
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT)); assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT));
@ -406,7 +406,7 @@ public class PresentationModelBinderTests {
public void bindToMapSingleString() { public void bindToMapSingleString() {
Map<String, String> values = new LinkedHashMap<String, String>(); Map<String, String> values = new LinkedHashMap<String, String>();
values.put("favoriteFoodsByGroup", "DAIRY=Milk FRUIT=Peaches MEAT=Ham"); values.put("favoriteFoodsByGroup", "DAIRY=Milk FRUIT=Peaches MEAT=Ham");
binder.bind(values); binder.bind(values, presentationModel);
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size()); Assert.assertEquals(3, bean.favoriteFoodsByGroup.size());
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY)); assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY));
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT)); assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT));
@ -429,7 +429,7 @@ public class PresentationModelBinderTests {
public void bindToNullObjectPath() { public void bindToNullObjectPath() {
Map<String, String> values = new LinkedHashMap<String, String>(); Map<String, String> values = new LinkedHashMap<String, String>();
values.put("primaryAddress.city", "Melbourne"); values.put("primaryAddress.city", "Melbourne");
binder.bind(values); binder.bind(values, presentationModel);
Assert.assertEquals("Melbourne", bean.primaryAddress.city); Assert.assertEquals("Melbourne", bean.primaryAddress.city);
} }

View File

@ -34,7 +34,7 @@ public class WebBinderTests {
public void setUp() { public void setUp() {
LocaleContextHolder.setLocale(Locale.US); LocaleContextHolder.setLocale(Locale.US);
presentationModel = new DefaultPresentationModel(bean); presentationModel = new DefaultPresentationModel(bean);
binder = new WebBinder(presentationModel); binder = new WebBinder();
} }
@After @After
@ -56,7 +56,7 @@ public class WebBinderTests {
userMap.put("!currency", "$5.00"); userMap.put("!currency", "$5.00");
userMap.put("_currency", "doesn't matter"); userMap.put("_currency", "doesn't matter");
userMap.put("_addresses", "doesn't matter"); userMap.put("_addresses", "doesn't matter");
BindingResults results = binder.bind(userMap); BindingResults results = binder.bind(userMap, presentationModel);
assertEquals(6, results.size()); assertEquals(6, results.size());
assertEquals("test", results.get(0).getSubmittedValue()); assertEquals("test", results.get(0).getSubmittedValue());
assertEquals(null, results.get(1).getSubmittedValue()); assertEquals(null, results.get(1).getSubmittedValue());