binder addition
This commit is contained in:
@@ -168,11 +168,23 @@ public class GenericConversionService implements ConversionService {
|
||||
|
||||
public ConversionExecutor getConversionExecutor(String id, Class sourceClass, Class targetClass)
|
||||
throws ConversionExecutorNotFoundException {
|
||||
Assert.hasText(id, "The id of the custom converter is required");
|
||||
Assert.notNull(sourceClass, "The source class to convert from is required");
|
||||
Assert.notNull(targetClass, "The target class to convert to is required");
|
||||
sourceClass = convertToWrapperClassIfNecessary(sourceClass);
|
||||
targetClass = convertToWrapperClassIfNecessary(targetClass);
|
||||
if (targetClass.isAssignableFrom(sourceClass)) {
|
||||
return new StaticConversionExecutor(sourceClass, targetClass, new NoOpConverter(sourceClass, targetClass));
|
||||
}
|
||||
Converter converter = (Converter) customConverters.get(id);
|
||||
if (converter == null) {
|
||||
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||
"No custom ConversionExecutor found with id '" + id + "' for converting from sourceClass ["
|
||||
+ sourceClass.getName() + "] to targetClass [" + targetClass.getName() + "]");
|
||||
if (parent != null) {
|
||||
return parent.getConversionExecutor(id, sourceClass, targetClass);
|
||||
} else {
|
||||
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||
"No custom ConversionExecutor found with id '" + id + "' for converting from sourceClass ["
|
||||
+ sourceClass.getName() + "] to targetClass [" + targetClass.getName() + "]");
|
||||
}
|
||||
}
|
||||
if (converter.getSourceClass().isAssignableFrom(sourceClass)) {
|
||||
if (!converter.getTargetClass().isAssignableFrom(targetClass)) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
|
||||
/**
|
||||
@@ -35,7 +36,7 @@ public class JsfViewFactoryCreator implements ViewFactoryCreator {
|
||||
private Lifecycle lifecycle;
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
return new JsfViewFactory(viewIdExpression, getLifecycle());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.springframework.faces.webflow.JsfManagedBeanAwareELExpressionParser;
|
||||
import org.springframework.faces.webflow.JsfViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
import org.springframework.webflow.expression.el.WebFlowELExpressionParser;
|
||||
|
||||
@@ -63,7 +64,7 @@ public class FacesFlowBuilderServicesBeanDefinitionParserTests extends TestCase
|
||||
public static class TestViewFactoryCreator implements ViewFactoryCreator {
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
|
||||
@@ -80,9 +80,7 @@ public class DojoDecorationRendererTests extends TestCase {
|
||||
childComponent.setConverter(converter);
|
||||
childComponent.setId("foo");
|
||||
Calendar cal = Calendar.getInstance(Locale.US);
|
||||
cal.set(Calendar.MONTH, Calendar.NOVEMBER);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 21);
|
||||
cal.set(Calendar.YEAR, 1977);
|
||||
cal.set(1977, Calendar.NOVEMBER, 21, 12, 0);
|
||||
childComponent.setValue(cal.getTime());
|
||||
DojoDecorationRenderer renderer = new DojoDecorationRenderer();
|
||||
String nodeAttributes = renderer.getNodeAttributesAsString(jsf.facesContext(), childComponent);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springframework.webflow.samples.booking;
|
||||
|
||||
import org.springframework.binding.convert.converters.StringToDate;
|
||||
import org.springframework.binding.convert.service.DefaultConversionService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("conversionService")
|
||||
public class ApplicationConversionService extends DefaultConversionService {
|
||||
|
||||
@Override
|
||||
protected void addDefaultConverters() {
|
||||
super.addDefaultConverters();
|
||||
addConverter("shortDate", new StringToDate());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
</webflow:flow-registry>
|
||||
|
||||
<!-- Plugs in a custom creator for Web Flow views -->
|
||||
<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator"/>
|
||||
<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator" conversion-service="conversionService"/>
|
||||
|
||||
<!-- Configures Web Flow to use Tiles to create views for rendering; Tiles allows for applying consistent layouts to your views -->
|
||||
<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
<binder>
|
||||
<binding property="checkinDate" converter="shortDate" required="true" />
|
||||
<binding property="checkoutDate" converter="shortDate" required="true" />
|
||||
<binding property="beds" required="true"/>
|
||||
<binding property="smoking" required="true" />
|
||||
<binding property="creditCard" required="true" />
|
||||
<binding property="creditCardName" required="true" />
|
||||
<binding property="creditCardExpirationMonth" required="true"/>
|
||||
<binding property="creditCardExpirationYear" required="true"/>
|
||||
<binding property="creditCardExpiryMonth" required="true"/>
|
||||
<binding property="creditCardExpiryYear" required="true"/>
|
||||
</binder>
|
||||
<transition on="proceed" to="reviewBooking" />
|
||||
<transition on="cancel" to="cancel" bind="false" />
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.webflow.engine.builder;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.View;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
|
||||
@@ -33,10 +34,11 @@ public interface ViewFactoryCreator {
|
||||
* @param viewId an expression that resolves the id of the view to render
|
||||
* @param expressionParser an optional expression parser to use to resolve view expressions
|
||||
* @param conversionService an optional conversion service to use to format text values
|
||||
* @param binderModel an optional configuration for how the rendered view binds to a model that provides its data
|
||||
* @return the view factory
|
||||
*/
|
||||
public ViewFactory createViewFactory(Expression viewId, ExpressionParser expressionParser,
|
||||
ConversionService conversionService);
|
||||
ConversionService conversionService, BinderModel binderModel);
|
||||
|
||||
/**
|
||||
* Get the default id of the view to render in the provided view state by convention.
|
||||
@@ -44,4 +46,5 @@ public interface ViewFactoryCreator {
|
||||
* @return the default view id
|
||||
*/
|
||||
public String getViewIdByConvention(String viewStateId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -75,6 +75,7 @@ import org.springframework.webflow.engine.model.AbstractStateModel;
|
||||
import org.springframework.webflow.engine.model.ActionStateModel;
|
||||
import org.springframework.webflow.engine.model.AttributeModel;
|
||||
import org.springframework.webflow.engine.model.BeanImportModel;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.engine.model.DecisionStateModel;
|
||||
import org.springframework.webflow.engine.model.EndStateModel;
|
||||
import org.springframework.webflow.engine.model.EvaluateModel;
|
||||
@@ -513,7 +514,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
}
|
||||
|
||||
private void parseAndAddViewState(ViewStateModel state, Flow flow) {
|
||||
ViewFactory viewFactory = parseViewFactory(state.getView(), state.getId(), false);
|
||||
ViewFactory viewFactory = parseViewFactory(state.getView(), state.getId(), false, state.getBinder());
|
||||
Boolean redirect = null;
|
||||
if (StringUtils.hasText(state.getRedirect())) {
|
||||
redirect = (Boolean) fromStringTo(Boolean.class).execute(state.getRedirect());
|
||||
@@ -571,7 +572,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
}
|
||||
parseAndPutSecured(state.getSecured(), attributes);
|
||||
Action finalResponseAction;
|
||||
ViewFactory viewFactory = parseViewFactory(state.getView(), state.getId(), true);
|
||||
ViewFactory viewFactory = parseViewFactory(state.getView(), state.getId(), true, null);
|
||||
if (viewFactory != null) {
|
||||
finalResponseAction = new ViewFactoryActionAdapter(viewFactory);
|
||||
} else {
|
||||
@@ -583,7 +584,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
attributes);
|
||||
}
|
||||
|
||||
private ViewFactory parseViewFactory(String view, String stateId, boolean endState) {
|
||||
private ViewFactory parseViewFactory(String view, String stateId, boolean endState, BinderModel binderModel) {
|
||||
if (!StringUtils.hasText(view)) {
|
||||
if (endState) {
|
||||
return null;
|
||||
@@ -591,7 +592,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
view = getLocalContext().getViewFactoryCreator().getViewIdByConvention(stateId);
|
||||
Expression viewId = getLocalContext().getExpressionParser().parseExpression(view,
|
||||
new FluentParserContext().template().evaluate(RequestContext.class).expectResult(String.class));
|
||||
return createViewFactory(viewId);
|
||||
return createViewFactory(viewId, binderModel);
|
||||
}
|
||||
} else if (view.startsWith("externalRedirect:")) {
|
||||
String encodedUrl = view.substring("externalRedirect:".length());
|
||||
@@ -606,13 +607,13 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
||||
} else {
|
||||
Expression viewId = getLocalContext().getExpressionParser().parseExpression(view,
|
||||
new FluentParserContext().template().evaluate(RequestContext.class).expectResult(String.class));
|
||||
return createViewFactory(viewId);
|
||||
return createViewFactory(viewId, binderModel);
|
||||
}
|
||||
}
|
||||
|
||||
private ViewFactory createViewFactory(Expression viewId) {
|
||||
private ViewFactory createViewFactory(Expression viewId, BinderModel binderModel) {
|
||||
return getLocalContext().getViewFactoryCreator().createViewFactory(viewId,
|
||||
getLocalContext().getExpressionParser(), getLocalContext().getConversionService());
|
||||
getLocalContext().getExpressionParser(), getLocalContext().getConversionService(), binderModel);
|
||||
}
|
||||
|
||||
private ViewVariable[] parseViewVariables(List vars) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.webflow.engine.model;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
@@ -25,10 +26,28 @@ public class BinderModel extends AbstractModel {
|
||||
|
||||
private LinkedList bindings;
|
||||
|
||||
public void addBinding(BindingModel bindingModel) {
|
||||
if (bindings == null) {
|
||||
bindings = new LinkedList();
|
||||
}
|
||||
bindings.add(bindingModel);
|
||||
}
|
||||
|
||||
public LinkedList getBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
public BindingModel getBinding(String name) {
|
||||
Iterator it = bindings.iterator();
|
||||
while (it.hasNext()) {
|
||||
BindingModel binding = (BindingModel) it.next();
|
||||
if (name.equals(binding.getProperty())) {
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setBindings(LinkedList bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
@@ -41,4 +60,4 @@ public class BinderModel extends AbstractModel {
|
||||
BinderModel binder = (BinderModel) model;
|
||||
setBindings(merge(getBindings(), binder.getBindings()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,14 +28,18 @@ public class BindingModel extends AbstractModel {
|
||||
|
||||
private String converter;
|
||||
|
||||
private String required;
|
||||
|
||||
/**
|
||||
* Create a binding model
|
||||
* @param property the name of the bound property
|
||||
* @param converter the converter
|
||||
* @param required required status
|
||||
*/
|
||||
public BindingModel(String property, String converter) {
|
||||
public BindingModel(String property, String converter, String required) {
|
||||
setProperty(property);
|
||||
setConverter(converter);
|
||||
setRequired(required);
|
||||
}
|
||||
|
||||
public boolean isMergeableWith(Model model) {
|
||||
@@ -87,4 +91,16 @@ public class BindingModel extends AbstractModel {
|
||||
}
|
||||
}
|
||||
|
||||
public String getRequired() {
|
||||
return required;
|
||||
}
|
||||
|
||||
public void setRequired(String required) {
|
||||
if (StringUtils.hasText(required)) {
|
||||
this.required = required;
|
||||
} else {
|
||||
this.required = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -543,7 +543,8 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
|
||||
}
|
||||
|
||||
private BindingModel parseBinding(Element element) {
|
||||
return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"));
|
||||
return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"), element
|
||||
.getAttribute("required"));
|
||||
}
|
||||
|
||||
private LinkedList parseOnExitActions(Element element) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
import org.springframework.webflow.mvc.portlet.PortletMvcViewFactory;
|
||||
import org.springframework.webflow.mvc.servlet.ServletMvcViewFactory;
|
||||
@@ -140,14 +141,14 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
|
||||
}
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewId, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
if (useSpringBeanBinding) {
|
||||
expressionParser = new BeanWrapperExpressionParser(conversionService);
|
||||
}
|
||||
if (environment == MvcEnvironment.SERVLET) {
|
||||
return new ServletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService);
|
||||
return new ServletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService, binderModel);
|
||||
} else if (environment == MvcEnvironment.PORTLET) {
|
||||
return new PortletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService);
|
||||
return new PortletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService, binderModel);
|
||||
} else {
|
||||
throw new IllegalStateException("Web MVC Environment " + environment + " not supported ");
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.mvc.view.AbstractMvcView;
|
||||
import org.springframework.webflow.mvc.view.AbstractMvcViewFactory;
|
||||
@@ -32,14 +33,15 @@ import org.springframework.webflow.mvc.view.FlowViewResolver;
|
||||
public class PortletMvcViewFactory extends AbstractMvcViewFactory {
|
||||
|
||||
public PortletMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
super(viewId, viewResolver, expressionParser, conversionService);
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
super(viewId, viewResolver, expressionParser, conversionService, binderModel);
|
||||
}
|
||||
|
||||
protected AbstractMvcView createMvcView(View view, RequestContext context) {
|
||||
PortletMvcView mvcView = new PortletMvcView(view, context);
|
||||
mvcView.setExpressionParser(getExpressionParser());
|
||||
mvcView.setConversionService(getConversionService());
|
||||
mvcView.setBinderModel(getBinderModel());
|
||||
return mvcView;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,4 +49,5 @@ public class ServletMvcView extends AbstractMvcView {
|
||||
context.getActiveFlow().getApplicationContext());
|
||||
getView().render(model, request, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.mvc.view.AbstractMvcView;
|
||||
import org.springframework.webflow.mvc.view.AbstractMvcViewFactory;
|
||||
@@ -32,14 +33,15 @@ import org.springframework.webflow.mvc.view.FlowViewResolver;
|
||||
public class ServletMvcViewFactory extends AbstractMvcViewFactory {
|
||||
|
||||
public ServletMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
super(viewId, viewResolver, expressionParser, conversionService);
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
super(viewId, viewResolver, expressionParser, conversionService, binderModel);
|
||||
}
|
||||
|
||||
protected AbstractMvcView createMvcView(View view, RequestContext context) {
|
||||
ServletMvcView mvcView = new ServletMvcView(view, context);
|
||||
mvcView.setExpressionParser(getExpressionParser());
|
||||
mvcView.setConversionService(getConversionService());
|
||||
mvcView.setBinderModel(getBinderModel());
|
||||
return mvcView;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.Set;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.EvaluationException;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
@@ -51,6 +52,7 @@ import org.springframework.web.util.WebUtils;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.definition.TransitionableStateDefinition;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.FlowExecutionKey;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
@@ -84,10 +86,10 @@ public abstract class AbstractMvcView implements View {
|
||||
|
||||
private String eventId;
|
||||
|
||||
private Set allowedBindFields;
|
||||
|
||||
private String fieldMarkerPrefix = "_";
|
||||
|
||||
private BinderModel binderModel;
|
||||
|
||||
/**
|
||||
* Creates a new MVC view.
|
||||
* @param view the Spring MVC view to render
|
||||
@@ -114,6 +116,14 @@ public abstract class AbstractMvcView implements View {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration describing how this view should bind to its model to access data for rendering.
|
||||
* @param binderModel the model binder configuratio
|
||||
*/
|
||||
public void setBinderModel(BinderModel binderModel) {
|
||||
this.binderModel = binderModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a prefix that can be used for parameters that mark potentially empty fields, having "prefix + field" as
|
||||
* name. Such a marker parameter is checked by existence: You can send any value for it, for example "visible". This
|
||||
@@ -133,14 +143,6 @@ public abstract class AbstractMvcView implements View {
|
||||
this.fieldMarkerPrefix = fieldMarkerPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register fields that should be allowed for binding. Default is all fields.
|
||||
* @param allowedBindFields the set of field name strings to allow
|
||||
*/
|
||||
public void setAllowedBindFields(Set allowedBindFields) {
|
||||
this.allowedBindFields = allowedBindFields;
|
||||
}
|
||||
|
||||
public void render() throws IOException {
|
||||
Map model = new HashMap();
|
||||
model.putAll(flowScopes());
|
||||
@@ -178,9 +180,11 @@ public abstract class AbstractMvcView implements View {
|
||||
viewErrors = true;
|
||||
addErrorMessages(mappingResults);
|
||||
} else {
|
||||
validate(model);
|
||||
if (requestContext.getMessageContext().hasErrorMessages()) {
|
||||
viewErrors = true;
|
||||
if (shouldValidate(model)) {
|
||||
validate(model);
|
||||
if (requestContext.getMessageContext().hasErrorMessages()) {
|
||||
viewErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,6 +244,7 @@ public abstract class AbstractMvcView implements View {
|
||||
if (modelObject != null) {
|
||||
BindingModel bindingModel = new BindingModel(getModelExpression().getExpressionString(), modelObject,
|
||||
expressionParser, conversionService, requestContext.getMessageContext());
|
||||
bindingModel.setBinderModel(binderModel);
|
||||
bindingModel.setMappingResults(mappingResults);
|
||||
model.put(BindingResult.MODEL_KEY_PREFIX + getModelExpression().getExpressionString(), bindingModel);
|
||||
}
|
||||
@@ -264,7 +269,7 @@ public abstract class AbstractMvcView implements View {
|
||||
if (transition == null) {
|
||||
return true;
|
||||
}
|
||||
return transition.getAttributes().getBoolean("bind", Boolean.FALSE).booleanValue();
|
||||
return transition.getAttributes().getBoolean("bind", Boolean.TRUE).booleanValue();
|
||||
}
|
||||
|
||||
private MappingResults bind(Object model) {
|
||||
@@ -273,45 +278,65 @@ public abstract class AbstractMvcView implements View {
|
||||
}
|
||||
DefaultMapper mapper = new DefaultMapper();
|
||||
ParameterMap requestParameters = requestContext.getRequestParameters();
|
||||
addDefaultMappings(mapper, requestParameters.asMap().keySet(), model);
|
||||
if (binderModel != null) {
|
||||
addModelBindingMappings(mapper, requestParameters.asMap().keySet(), model);
|
||||
} else {
|
||||
addDefaultMappings(mapper, requestParameters.asMap().keySet(), model);
|
||||
}
|
||||
return mapper.map(requestParameters, model);
|
||||
}
|
||||
|
||||
private void addModelBindingMappings(DefaultMapper mapper, Set parameterNames, Object model) {
|
||||
Iterator it = binderModel.getBindings().iterator();
|
||||
while (it.hasNext()) {
|
||||
org.springframework.webflow.engine.model.BindingModel binding = (org.springframework.webflow.engine.model.BindingModel) it
|
||||
.next();
|
||||
String parameterName = binding.getProperty();
|
||||
if (parameterNames.contains(parameterName)) {
|
||||
addMapping(mapper, binding, model);
|
||||
} else {
|
||||
if (fieldMarkerPrefix != null && parameterNames.contains(fieldMarkerPrefix + parameterName)) {
|
||||
addEmptyValueMapping(mapper, parameterName, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addMapping(DefaultMapper mapper, org.springframework.webflow.engine.model.BindingModel binding,
|
||||
Object model) {
|
||||
Expression source = new RequestParameterExpression(binding.getProperty());
|
||||
ParserContext parserContext = new FluentParserContext().evaluate(model.getClass());
|
||||
Expression target = expressionParser.parseExpression(binding.getProperty(), parserContext);
|
||||
DefaultMapping mapping = new DefaultMapping(source, target);
|
||||
// TODO - this is inefficient - consider introducing a typed Binding object
|
||||
if (binding.getRequired() != null) {
|
||||
mapping.setRequired(Boolean.valueOf(binding.getRequired()).booleanValue());
|
||||
}
|
||||
if (binding.getConverter() != null) {
|
||||
ConversionExecutor conversionExecutor = conversionService.getConversionExecutor(binding.getConverter(),
|
||||
String.class, target.getValueType(model));
|
||||
mapping.setTypeConverter(conversionExecutor);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding mapping for parameter '" + binding.getProperty() + "'");
|
||||
}
|
||||
mapper.addMapping(mapping);
|
||||
}
|
||||
|
||||
private void addDefaultMappings(DefaultMapper mapper, Set parameterNames, Object model) {
|
||||
for (Iterator it = parameterNames.iterator(); it.hasNext();) {
|
||||
String parameterName = (String) it.next();
|
||||
if (fieldMarkerPrefix != null && parameterName.startsWith(fieldMarkerPrefix)) {
|
||||
String field = parameterName.substring(fieldMarkerPrefix.length());
|
||||
if (isAllowedField(field)) {
|
||||
if (!parameterNames.contains(field)) {
|
||||
addEmptyValueMapping(mapper, field, model);
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Will not map field marker parameter '" + parameterName + "'");
|
||||
}
|
||||
if (!parameterNames.contains(field)) {
|
||||
addEmptyValueMapping(mapper, field, model);
|
||||
}
|
||||
} else {
|
||||
if (isAllowedField(parameterName)) {
|
||||
addDefaultMapping(mapper, parameterName, model);
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Will not map parameter '" + parameterName + "'");
|
||||
}
|
||||
}
|
||||
addDefaultMapping(mapper, parameterName, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAllowedField(String field) {
|
||||
if (allowedBindFields == null) {
|
||||
// always allow
|
||||
return true;
|
||||
} else {
|
||||
return allowedBindFields.contains(field);
|
||||
}
|
||||
}
|
||||
|
||||
private void addEmptyValueMapping(DefaultMapper mapper, String field, Object model) {
|
||||
ParserContext parserContext = new FluentParserContext().evaluate(model.getClass());
|
||||
Expression target = expressionParser.parseExpression(field, parserContext);
|
||||
@@ -320,7 +345,7 @@ public abstract class AbstractMvcView implements View {
|
||||
Expression source = new StaticExpression(getEmptyValue(propertyType));
|
||||
DefaultMapping mapping = new DefaultMapping(source, target);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding empty parameter value mapping for field '" + field + "'");
|
||||
logger.debug("Adding empty value mapping for parameter '" + field + "'");
|
||||
}
|
||||
mapper.addMapping(mapping);
|
||||
} catch (EvaluationException e) {
|
||||
@@ -347,7 +372,7 @@ public abstract class AbstractMvcView implements View {
|
||||
Expression target = expressionParser.parseExpression(parameter, parserContext);
|
||||
DefaultMapping mapping = new DefaultMapping(source, target);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding mapping for parameter '" + parameter + "'");
|
||||
logger.debug("Adding default mapping for parameter '" + parameter + "'");
|
||||
}
|
||||
mapper.addMapping(mapping);
|
||||
}
|
||||
@@ -377,6 +402,15 @@ public abstract class AbstractMvcView implements View {
|
||||
.defaultText(errorCode + " on " + field).build();
|
||||
}
|
||||
|
||||
private boolean shouldValidate(Object model) {
|
||||
TransitionableStateDefinition currentState = (TransitionableStateDefinition) requestContext.getCurrentState();
|
||||
TransitionDefinition transition = currentState.getTransition(eventId);
|
||||
if (transition == null) {
|
||||
return true;
|
||||
}
|
||||
return transition.getAttributes().getBoolean("validate", Boolean.TRUE).booleanValue();
|
||||
}
|
||||
|
||||
private void validate(Object model) {
|
||||
String validateMethodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId());
|
||||
Method validateMethod = ReflectionUtils.findMethod(model.getClass(), validateMethodName,
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.webflow.mvc.view;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.View;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
@@ -37,12 +38,15 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private BinderModel binderModel;
|
||||
|
||||
public AbstractMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
this.viewId = viewId;
|
||||
this.viewResolver = viewResolver;
|
||||
this.expressionParser = expressionParser;
|
||||
this.conversionService = conversionService;
|
||||
this.binderModel = binderModel;
|
||||
}
|
||||
|
||||
protected ExpressionParser getExpressionParser() {
|
||||
@@ -53,6 +57,10 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
protected BinderModel getBinderModel() {
|
||||
return binderModel;
|
||||
}
|
||||
|
||||
public View getView(RequestContext context) {
|
||||
String viewId = (String) this.viewId.getValue(context);
|
||||
org.springframework.web.servlet.View view = viewResolver.resolveView(viewId, context);
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.validation.AbstractErrors;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
|
||||
/**
|
||||
* Makes the properties of the "model" object available to Spring views during rendering. Also makes data binding (aka
|
||||
@@ -63,6 +64,8 @@ public class BindingModel extends AbstractErrors {
|
||||
|
||||
private MessageContext messageContext;
|
||||
|
||||
private BinderModel binderModel;
|
||||
|
||||
/**
|
||||
* Creates a new Spring Binding model.
|
||||
* @param objectName the name of the bound model object
|
||||
@@ -91,6 +94,10 @@ public class BindingModel extends AbstractErrors {
|
||||
this.mappingResults = results;
|
||||
}
|
||||
|
||||
public void setBinderModel(BinderModel binderModel) {
|
||||
this.binderModel = binderModel;
|
||||
}
|
||||
|
||||
// implementing Errors
|
||||
|
||||
public List getAllErrors() {
|
||||
@@ -144,12 +151,23 @@ public class BindingModel extends AbstractErrors {
|
||||
private ConversionExecutor getConverter(Expression fieldExpression) {
|
||||
if (conversionService != null) {
|
||||
Class valueType = fieldExpression.getValueType(boundObject);
|
||||
// TODO -- this really is not accurate for Collection or Array or Map types
|
||||
// This needs to be cleaned up
|
||||
// special handling for array, collection, map types
|
||||
// necessary as getFieldValue is called by form tags for non-formattable properties, too
|
||||
// TODO - investigate how to improve this in Spring MVC
|
||||
if (valueType.isArray() || Collection.class.isAssignableFrom(valueType)
|
||||
|| Map.class.isAssignableFrom(valueType)) {
|
||||
return null;
|
||||
}
|
||||
if (binderModel != null) {
|
||||
org.springframework.webflow.engine.model.BindingModel binding = binderModel.getBinding(fieldExpression
|
||||
.getExpressionString());
|
||||
if (binding != null) {
|
||||
String converterId = binding.getConverter();
|
||||
if (converterId != null) {
|
||||
return conversionService.getConversionExecutor(converterId, valueType, String.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return conversionService.getConversionExecutor(valueType, String.class);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.View;
|
||||
@@ -36,7 +37,7 @@ import org.springframework.webflow.execution.ViewFactory;
|
||||
class MockViewFactoryCreator implements ViewFactoryCreator {
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewId, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
return new MockViewFactory(viewId);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
import org.springframework.webflow.mvc.builder.MvcViewFactoryCreator;
|
||||
|
||||
@@ -46,7 +47,7 @@ public class FlowBuilderServicesBeanDefinitionParserTests extends TestCase {
|
||||
public static class TestViewFactoryCreator implements ViewFactoryCreator {
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
|
||||
ConversionService conversionService) {
|
||||
ConversionService conversionService, BinderModel binderModel) {
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.webflow.action.ViewFactoryActionAdapter;
|
||||
import org.springframework.webflow.engine.EndState;
|
||||
import org.springframework.webflow.engine.StubViewFactory;
|
||||
import org.springframework.webflow.engine.ViewState;
|
||||
import org.springframework.webflow.engine.model.BinderModel;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.test.MockFlowExecutionKey;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
@@ -214,9 +215,10 @@ public class MvcViewTests extends TestCase {
|
||||
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1"));
|
||||
org.springframework.web.servlet.View mvcView = new MockView();
|
||||
AbstractMvcView view = new MockMvcView(mvcView, context);
|
||||
HashSet allowedBindFields = new HashSet();
|
||||
allowedBindFields.add("stringProperty");
|
||||
view.setAllowedBindFields(allowedBindFields);
|
||||
BinderModel binderModel = new BinderModel();
|
||||
binderModel
|
||||
.addBinding(new org.springframework.webflow.engine.model.BindingModel("stringProperty", null, "true"));
|
||||
view.setBinderModel(binderModel);
|
||||
view.processUserEvent();
|
||||
assertTrue(view.hasFlowEvent());
|
||||
assertEquals("submit", view.getFlowEvent().getId());
|
||||
@@ -246,7 +248,6 @@ public class MvcViewTests extends TestCase {
|
||||
AbstractMvcView view = new MockMvcView(mvcView, context);
|
||||
HashSet allowedBindFields = new HashSet();
|
||||
allowedBindFields.add("booleanProperty");
|
||||
view.setAllowedBindFields(allowedBindFields);
|
||||
view.processUserEvent();
|
||||
assertEquals(false, bindBean.getBooleanProperty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user