This commit is contained in:
Keith Donald 2009-07-25 04:40:31 +00:00
parent 726d3464c1
commit 990d446c84
13 changed files with 135 additions and 85 deletions

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.model.ui.binder; package org.springframework.model.binder.support;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -24,32 +24,21 @@ 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.BindingResults;
import org.springframework.model.binder.MissingFieldException; import org.springframework.model.binder.MissingFieldException;
import org.springframework.model.ui.BindingStatus;
import org.springframework.model.ui.FieldModel;
import org.springframework.model.ui.FieldNotFoundException;
import org.springframework.model.ui.PresentationModel;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* A generic {@link Binder binder} suitable for use in most environments. * A base {@link Binder binder} implementation designed for subclassing.
* @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)
*/ */
public class PresentationModelBinder implements Binder { public abstract class AbstractBinder implements Binder {
private PresentationModel presentationModel;
private String[] requiredFields; private String[] requiredFields;
private MessageSource messageSource;
public PresentationModelBinder(PresentationModel presentationModel) { private MessageSource messageSource;
Assert.notNull(presentationModel, "The PresentationModel is required");
this.presentationModel = presentationModel;
}
/** /**
* Configure the MessageSource that resolves localized {@link BindingResult} alert messages. * Configure the MessageSource that resolves localized {@link BindingResult} alert messages.
@ -59,7 +48,7 @@ public class PresentationModelBinder implements Binder {
Assert.notNull(messageSource, "The MessageSource is required"); Assert.notNull(messageSource, "The MessageSource is required");
this.messageSource = messageSource; this.messageSource = messageSource;
} }
/** /**
* 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 field names * @param fieldNames the field names
@ -68,18 +57,6 @@ public class PresentationModelBinder implements Binder {
public void setRequiredFields(String[] fieldNames) { public void setRequiredFields(String[] fieldNames) {
this.requiredFields = fieldNames; this.requiredFields = fieldNames;
} }
// subclassing hooks
/**
* Get the model for the field.
* @param fieldName
* @return the field model
* @throws NoSuchFieldException if no such field exists
*/
protected FieldModel getFieldModel(String fieldName) {
return presentationModel.getFieldModel(fieldName);
}
// implementing Binder // implementing Binder
@ -88,18 +65,14 @@ public class PresentationModelBinder implements Binder {
checkRequired(fieldValues); checkRequired(fieldValues);
ArrayListBindingResults results = new ArrayListBindingResults(fieldValues.size()); ArrayListBindingResults results = new ArrayListBindingResults(fieldValues.size());
for (Map.Entry<String, ? extends Object> fieldValue : fieldValues.entrySet()) { for (Map.Entry<String, ? extends Object> fieldValue : fieldValues.entrySet()) {
try { results.add(bind(fieldValue));
FieldModel field = getFieldModel(fieldValue.getKey());
results.add(bind(fieldValue, field));
} catch (FieldNotFoundException e) {
results.add(new FieldNotFoundResult(fieldValue.getKey(), fieldValue.getValue(), messageSource));
}
} }
return results; return results;
} }
// subclassing hooks // subclassing hooks
/** /**
* Hook subclasses may use to filter the source values to bind. * Hook subclasses may use to filter the source values to bind.
* This hook allows the binder to pre-process the field values before binding occurs. * This hook allows the binder to pre-process the field values before binding occurs.
@ -112,6 +85,20 @@ public class PresentationModelBinder implements Binder {
return fieldValues; return fieldValues;
} }
/**
* The configured MessageSource that resolves binding result alert messages.
*/
protected MessageSource getMessageSource() {
return messageSource;
}
/**
* Hook method subclasses should override to perform a single binding.
* @param fieldValue the field value to bind
* @return the binding result
*/
protected abstract BindingResult bind(Map.Entry<String, ? extends Object> fieldValue);
// internal helpers // internal helpers
private void checkRequired(Map<String, ? extends Object> fieldValues) { private void checkRequired(Map<String, ? extends Object> fieldValues) {
@ -135,18 +122,4 @@ public class PresentationModelBinder implements Binder {
} }
} }
private BindingResult bind(Map.Entry<String, ? extends Object> fieldValue, FieldModel field) {
String fieldName = fieldValue.getKey();
Object value = fieldValue.getValue();
if (!field.isEditable()) {
return new FieldNotEditableResult(fieldName, value, messageSource);
} else {
field.applySubmittedValue(value);
if (field.getBindingStatus() == BindingStatus.DIRTY) {
field.commit();
}
return new BindingStatusResult(fieldName, value, field.getStatusAlert());
}
}
} }

View File

@ -13,24 +13,24 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.model.ui.binder; package org.springframework.model.binder.support;
import org.springframework.model.alert.Alert; import org.springframework.model.alert.Alert;
import org.springframework.model.alert.Severity; import org.springframework.model.alert.Severity;
import org.springframework.model.binder.BindingResult; import org.springframework.model.binder.BindingResult;
class BindingStatusResult implements BindingResult { public class AlertBindingResult implements BindingResult {
private String fieldName; private String fieldName;
private Object sourceValue; private Object submittedValue;
private Alert bindingStatusAlert; private Alert alert;
public BindingStatusResult(String fieldName, Object sourceValue, Alert alert) { public AlertBindingResult(String fieldName, Object sourceValue, Alert alert) {
this.fieldName = fieldName; this.fieldName = fieldName;
this.sourceValue = sourceValue; this.submittedValue = sourceValue;
this.bindingStatusAlert = alert; this.alert = alert;
} }
public String getFieldName() { public String getFieldName() {
@ -38,15 +38,15 @@ class BindingStatusResult implements BindingResult {
} }
public Object getSubmittedValue() { public Object getSubmittedValue() {
return sourceValue; return submittedValue;
} }
public boolean isFailure() { public boolean isFailure() {
return bindingStatusAlert.getSeverity().compareTo(Severity.INFO) > 1; return alert.getSeverity().compareTo(Severity.INFO) > 1;
} }
public Alert getAlert() { public Alert getAlert() {
return bindingStatusAlert; return alert;
} }
public String toString() { public String toString() {

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.model.ui.binder; package org.springframework.model.binder.support;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.model.ui.binder; package org.springframework.model.binder.support;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.core.style.StylerUtils; import org.springframework.core.style.StylerUtils;
@ -23,17 +23,17 @@ import org.springframework.model.binder.BindingResult;
import org.springframework.model.message.MessageBuilder; import org.springframework.model.message.MessageBuilder;
import org.springframework.model.message.ResolvableArgument; import org.springframework.model.message.ResolvableArgument;
class FieldNotEditableResult implements BindingResult { public class FieldNotEditableResult implements BindingResult {
private String property; private String property;
private Object sourceValue; private Object submittedValue;
private MessageSource messageSource; private MessageSource messageSource;
public FieldNotEditableResult(String property, Object sourceValue, MessageSource messageSource) { public FieldNotEditableResult(String property, Object submittedValue, MessageSource messageSource) {
this.property = property; this.property = property;
this.sourceValue = sourceValue; this.submittedValue = submittedValue;
this.messageSource = messageSource; this.messageSource = messageSource;
} }
@ -42,7 +42,7 @@ class FieldNotEditableResult implements BindingResult {
} }
public Object getSubmittedValue() { public Object getSubmittedValue() {
return sourceValue; return submittedValue;
} }
public boolean isFailure() { public boolean isFailure() {
@ -63,9 +63,9 @@ class FieldNotEditableResult implements BindingResult {
MessageBuilder builder = new MessageBuilder(messageSource); MessageBuilder builder = new MessageBuilder(messageSource);
builder.code("bindSuccess"); builder.code("bindSuccess");
builder.arg("label", new ResolvableArgument(property)); builder.arg("label", new ResolvableArgument(property));
builder.arg("value", sourceValue); builder.arg("value", submittedValue);
// TODO lazily create default message // TODO lazily create default message
builder.defaultMessage("Successfully bound user value " + StylerUtils.style(sourceValue) builder.defaultMessage("Successfully bound user value " + StylerUtils.style(submittedValue)
+ " to property '" + property + "'"); + " to property '" + property + "'");
return builder.build(); return builder.build();
} }

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.model.ui.binder; package org.springframework.model.binder.support;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.core.style.StylerUtils; import org.springframework.core.style.StylerUtils;
@ -23,17 +23,17 @@ import org.springframework.model.binder.BindingResult;
import org.springframework.model.message.MessageBuilder; import org.springframework.model.message.MessageBuilder;
import org.springframework.model.message.ResolvableArgument; import org.springframework.model.message.ResolvableArgument;
class FieldNotFoundResult implements BindingResult { public class FieldNotFoundResult implements BindingResult {
private String property; private String property;
private Object sourceValue; private Object submittedValue;
private MessageSource messageSource; private MessageSource messageSource;
public FieldNotFoundResult(String property, Object sourceValue, MessageSource messageSource) { public FieldNotFoundResult(String property, Object submittedValue, MessageSource messageSource) {
this.property = property; this.property = property;
this.sourceValue = sourceValue; this.submittedValue = submittedValue;
this.messageSource = messageSource; this.messageSource = messageSource;
} }
@ -42,7 +42,7 @@ class FieldNotFoundResult implements BindingResult {
} }
public Object getSubmittedValue() { public Object getSubmittedValue() {
return sourceValue; return submittedValue;
} }
public boolean isFailure() { public boolean isFailure() {
@ -63,9 +63,9 @@ class FieldNotFoundResult implements BindingResult {
MessageBuilder builder = new MessageBuilder(messageSource); MessageBuilder builder = new MessageBuilder(messageSource);
builder.code("bindSuccess"); builder.code("bindSuccess");
builder.arg("label", new ResolvableArgument(property)); builder.arg("label", new ResolvableArgument(property));
builder.arg("value", sourceValue); builder.arg("value", submittedValue);
// TODO lazily create default message // TODO lazily create default message
builder.defaultMessage("Successfully bound user value " + StylerUtils.style(sourceValue) builder.defaultMessage("Successfully bound user value " + StylerUtils.style(submittedValue)
+ " to property '" + property + "'"); + " to property '" + property + "'");
return builder.build(); return builder.build();
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.springframework.model.ui; package org.springframework.model.ui;
/** /**
* Thrown when a PresentationModel field cannot be found. * Thrown when a PresentationModel field cannot be found.
* @author Keith Donald * @author Keith Donald

View File

@ -15,6 +15,7 @@
*/ */
package org.springframework.model.ui; package org.springframework.model.ui;
/** /**
* Represents the state and behavior of a presentation independently of the GUI controls used in the interface. * Represents the state and behavior of a presentation independently of the GUI controls used in the interface.
* Pulls the state and behavior of a view out into a model class that is part of the presentation. * Pulls the state and behavior of a view out into a model class that is part of the presentation.

View File

@ -1,5 +0,0 @@
/**
* Binder implementation that binds to PresentationModels.
*/
package org.springframework.model.ui.binder;

View File

@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.model.ui.binder; package org.springframework.model.ui.config;
import org.springframework.model.binder.Binder; import org.springframework.model.binder.Binder;
import org.springframework.model.binder.BindingResults; import org.springframework.model.binder.BindingResults;
import org.springframework.model.ui.config.FieldModelConfiguration;
/** /**
* A SPI interface that lets you configure a {@link Binder}, then execute it. * A SPI interface that lets you configure a {@link Binder}, then execute it.

View File

@ -0,0 +1,81 @@
/*
* 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.ui.support;
import java.util.Map;
import org.springframework.context.MessageSource;
import org.springframework.model.binder.Binder;
import org.springframework.model.binder.BindingResult;
import org.springframework.model.binder.support.AbstractBinder;
import org.springframework.model.binder.support.AlertBindingResult;
import org.springframework.model.binder.support.FieldNotEditableResult;
import org.springframework.model.binder.support.FieldNotFoundResult;
import org.springframework.model.ui.BindingStatus;
import org.springframework.model.ui.FieldModel;
import org.springframework.model.ui.FieldNotFoundException;
import org.springframework.model.ui.PresentationModel;
import org.springframework.util.Assert;
/**
* A generic {@link Binder binder} suitable for use in most environments.
* @author Keith Donald
* @since 3.0
* @see #setMessageSource(MessageSource)
* @see #setRequiredFields(String[])
* @see #bind(Map)
*/
public class PresentationModelBinder extends AbstractBinder {
private PresentationModel presentationModel;
public PresentationModelBinder(PresentationModel presentationModel) {
Assert.notNull(presentationModel, "The PresentationModel is required");
this.presentationModel = presentationModel;
}
// subclassing hooks
/**
* Get the model for the field.
* @param fieldName
* @return the field model
* @throws NoSuchFieldException if no such field exists
*/
protected FieldModel getFieldModel(String fieldName) {
return presentationModel.getFieldModel(fieldName);
}
protected BindingResult bind(Map.Entry<String, ? extends Object> fieldValue) {
String fieldName = fieldValue.getKey();
Object value = fieldValue.getValue();
FieldModel field;
try {
field = getFieldModel(fieldName);
} catch (FieldNotFoundException e) {
return new FieldNotFoundResult(fieldName, value, getMessageSource());
}
if (!field.isEditable()) {
return new FieldNotEditableResult(fieldName, value, getMessageSource());
} else {
field.applySubmittedValue(value);
if (field.getBindingStatus() == BindingStatus.DIRTY) {
field.commit();
}
return new AlertBindingResult(fieldName, value, field.getStatusAlert());
}
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.model.ui.binder; package org.springframework.model.ui.support;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;

View File

@ -27,7 +27,6 @@ import org.springframework.model.binder.MissingFieldException;
import org.springframework.model.message.MockMessageSource; import org.springframework.model.message.MockMessageSource;
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.binder.PresentationModelBinder;
import org.springframework.model.ui.format.AnnotationFormatterFactory; import org.springframework.model.ui.format.AnnotationFormatterFactory;
import org.springframework.model.ui.format.Formatted; import org.springframework.model.ui.format.Formatted;
import org.springframework.model.ui.format.Formatter; import org.springframework.model.ui.format.Formatter;
@ -38,6 +37,7 @@ import org.springframework.model.ui.format.number.IntegerFormatter;
import org.springframework.model.ui.support.CollectionTypeDescriptor; import org.springframework.model.ui.support.CollectionTypeDescriptor;
import org.springframework.model.ui.support.DefaultPresentationModel; import org.springframework.model.ui.support.DefaultPresentationModel;
import org.springframework.model.ui.support.GenericFormatterRegistry; import org.springframework.model.ui.support.GenericFormatterRegistry;
import org.springframework.model.ui.support.PresentationModelBinder;
public class GenericBinderTests { public class GenericBinderTests {

View File

@ -15,12 +15,12 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.model.binder.BindingResults; import org.springframework.model.binder.BindingResults;
import org.springframework.model.ui.binder.WebBinder;
import org.springframework.model.ui.format.date.DateFormatter; import org.springframework.model.ui.format.date.DateFormatter;
import org.springframework.model.ui.format.number.CurrencyFormat; import org.springframework.model.ui.format.number.CurrencyFormat;
import org.springframework.model.ui.format.number.CurrencyFormatter; import org.springframework.model.ui.format.number.CurrencyFormatter;
import org.springframework.model.ui.support.DefaultPresentationModel; import org.springframework.model.ui.support.DefaultPresentationModel;
import org.springframework.model.ui.support.GenericFormatterRegistry; import org.springframework.model.ui.support.GenericFormatterRegistry;
import org.springframework.model.ui.support.WebBinder;
public class WebBinderTests { public class WebBinderTests {