diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/BindingModel.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/BindingModel.java
index c7051dbf..c60b6c6f 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/BindingModel.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/BindingModel.java
@@ -28,7 +28,7 @@ public class BindingModel extends AbstractModel {
private String converter;
- private String required;
+ private boolean required;
/**
* Create a binding model
@@ -36,7 +36,7 @@ public class BindingModel extends AbstractModel {
* @param converter the converter
* @param required required status
*/
- public BindingModel(String property, String converter, String required) {
+ public BindingModel(String property, String converter, boolean required) {
setProperty(property);
setConverter(converter);
setRequired(required);
@@ -91,16 +91,12 @@ public class BindingModel extends AbstractModel {
}
}
- public String getRequired() {
+ public boolean getRequired() {
return required;
}
- public void setRequired(String required) {
- if (StringUtils.hasText(required)) {
- this.required = required;
- } else {
- this.required = null;
- }
+ public void setRequired(boolean required) {
+ this.required = required;
}
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
index a3c14a3c..67b098ab 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
@@ -543,8 +543,8 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
}
private BindingModel parseBinding(Element element) {
- return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"), element
- .getAttribute("required"));
+ boolean required = Boolean.valueOf(element.getAttribute("required")).booleanValue();
+ return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"), required);
}
private LinkedList parseOnExitActions(Element element) {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java
index 0ad9895a..04366e31 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java
@@ -24,6 +24,7 @@ import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.expression.beanwrapper.BeanWrapperExpressionParser;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
+import org.springframework.util.StringUtils;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
@@ -31,6 +32,7 @@ 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;
+import org.springframework.webflow.mvc.view.AbstractMvcViewFactory;
import org.springframework.webflow.mvc.view.FlowViewResolver;
/**
@@ -60,32 +62,23 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
private boolean useSpringBeanBinding;
+ private String eventIdParameterName;
+
+ private String fieldMarkerPrefix;
+
/**
* Create a new Spring MVC View Factory Creator.
* @see #setDefaultViewSuffix(String)
- * @see #setViewResolvers(List)
+ * @see #setEventIdParameterName(String)
+ * @see #setFieldMarkerPrefix(String)
+ * @see #setUseSpringBeanBinding(boolean)
* @see #setFlowViewResolver(FlowViewResolver)
+ * @see #setViewResolvers(List)
*/
public MvcViewFactoryCreator() {
}
- /**
- * Returns the configured mvc environment.
- * @return the mvc environment
- */
- public MvcEnvironment getEnvironment() {
- return environment;
- }
-
- /**
- * Sets the configured mvc environment.
- * @param environment the mvc environment.
- */
- public void setEnvironment(MvcEnvironment environment) {
- this.environment = environment;
- }
-
/**
* Configure an {@link FlowResourceFlowViewResolver} capable of resolving view resources by applying the specified
* default resource suffix. Default is .jsp.
@@ -98,13 +91,41 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
}
/**
- * Sets the chain of Spring MVC {@link ViewResolver view resolvers} to delegate to resolve views selected by flows.
- * Allows for reuse of existing View Resolvers configured in a Spring application context. If multiple resolvers are
- * to be used, the resolvers should be ordered in the manner they should be applied.
- * @param viewResolvers the view resolver list
+ * Sets the name of the request parameter to use to lookup user events signaled by views created in this factory. If
+ * not specified, the default is _eventId
+ * @param eventIdParameterName the event id parameter name
*/
- public void setViewResolvers(List viewResolvers) {
- this.flowViewResolver = new DelegatingFlowViewResolver(viewResolvers);
+ public void setEventIdParameterName(String eventIdParameterName) {
+ this.eventIdParameterName = eventIdParameterName;
+ }
+
+ /**
+ * 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
+ * is particularly useful for HTML checkboxes and select options.
+ *
+ * Default is "_", for "_FIELD" parameters (e.g. "_subscribeToNewsletter"). Set this to null if you want to turn off + * the empty field check completely. + *
+ * HTML checkboxes only send a value when they're checked, so it is not possible to detect that a formerly checked + * box has just been unchecked, at least not with standard HTML means. + *
+ * This auto-reset mechanism addresses this deficiency, provided that a marker parameter is sent for each checkbox
+ * field, like "_subscribeToNewsletter" for a "subscribeToNewsletter" field. As the marker parameter is sent in any
+ * case, the data binder can detect an empty field and automatically reset its value.
+ */
+ public void setFieldMarkerPrefix(String fieldMarkerPrefix) {
+ this.fieldMarkerPrefix = fieldMarkerPrefix;
+ }
+
+ /**
+ * Sets whether to use data binding with Spring's {@link BeanWrapper} should be enabled. Set to 'true' to enable.
+ * 'false', disabled, is the default. With this enabled, the same binding system used by Spring MVC 2.x is also used
+ * in a Web Flow environment.
+ * @param useSpringBeanBinding the Spring bean binding flag
+ */
+ public void setUseSpringBeanBinding(boolean useSpringBeanBinding) {
+ this.useSpringBeanBinding = useSpringBeanBinding;
}
/**
@@ -116,22 +137,13 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
}
/**
- * Whether data binding with Spring's {@link BeanWrapper} should be enabled. Default is false. With this enabled,
- * the same binding system used by Spring MVC 2.x is also used in a Web Flow environment.
- * @return the use Spring bean binding flag
+ * Sets the chain of Spring MVC {@link ViewResolver view resolvers} to delegate to resolve views selected by flows.
+ * Allows for reuse of existing View Resolvers configured in a Spring application context. If multiple resolvers are
+ * to be used, the resolvers should be ordered in the manner they should be applied.
+ * @param viewResolvers the view resolver list
*/
- public boolean getUseSpringBeanBinding() {
- return useSpringBeanBinding;
- }
-
- /**
- * Sets whether to use data binding with Spring's {@link BeanWrapper} should be enabled. Set to 'true' to enable.
- * 'false', disabled, is the default. With this enabled, the same binding system used by Spring MVC 2.x is also used
- * in a Web Flow environment.
- * @param useSpringBeanBinding the Spring bean binding flag
- */
- public void setUseSpringBeanBinding(boolean useSpringBeanBinding) {
- this.useSpringBeanBinding = useSpringBeanBinding;
+ public void setViewResolvers(List viewResolvers) {
+ this.flowViewResolver = new DelegatingFlowViewResolver(viewResolvers);
}
// implementing ApplicationContextAware
@@ -145,6 +157,19 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
if (useSpringBeanBinding) {
expressionParser = new BeanWrapperExpressionParser(conversionService);
}
+ AbstractMvcViewFactory viewFactory = createMvcViewFactory(viewId, expressionParser, conversionService,
+ binderModel);
+ if (StringUtils.hasText(eventIdParameterName)) {
+ viewFactory.setEventIdParameterName(eventIdParameterName);
+ }
+ if (StringUtils.hasText(fieldMarkerPrefix)) {
+ viewFactory.setFieldMarkerPrefix(fieldMarkerPrefix);
+ }
+ return viewFactory;
+ }
+
+ private AbstractMvcViewFactory createMvcViewFactory(Expression viewId, ExpressionParser expressionParser,
+ ConversionService conversionService, BinderModel binderModel) {
if (environment == MvcEnvironment.SERVLET) {
return new ServletMvcViewFactory(viewId, flowViewResolver, expressionParser, conversionService, binderModel);
} else if (environment == MvcEnvironment.PORTLET) {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcViewFactory.java
index 7ac3da89..890e5cfe 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcViewFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcViewFactory.java
@@ -38,11 +38,7 @@ public class PortletMvcViewFactory extends AbstractMvcViewFactory {
}
protected AbstractMvcView createMvcView(View view, RequestContext context) {
- PortletMvcView mvcView = new PortletMvcView(view, context);
- mvcView.setExpressionParser(getExpressionParser());
- mvcView.setConversionService(getConversionService());
- mvcView.setBinderModel(getBinderModel());
- return mvcView;
+ return new PortletMvcView(view, context);
}
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcViewFactory.java
index 94085036..9c770948 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcViewFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcViewFactory.java
@@ -38,11 +38,7 @@ public class ServletMvcViewFactory extends AbstractMvcViewFactory {
}
protected AbstractMvcView createMvcView(View view, RequestContext context) {
- ServletMvcView mvcView = new ServletMvcView(view, context);
- mvcView.setExpressionParser(getExpressionParser());
- mvcView.setConversionService(getConversionService());
- mvcView.setBinderModel(getBinderModel());
- return mvcView;
+ return new ServletMvcView(view, context);
}
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java
index 5ec586ce..19dd7184 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java
@@ -80,13 +80,15 @@ public abstract class AbstractMvcView implements View {
private ConversionService conversionService;
- private MappingResults mappingResults;
+ private String fieldMarkerPrefix = "_";
- private boolean viewErrors;
+ private String eventIdParameterName = "_eventId";
private String eventId;
- private String fieldMarkerPrefix = "_";
+ private MappingResults mappingResults;
+
+ private boolean viewErrors;
private BinderModel binderModel;
@@ -143,6 +145,15 @@ public abstract class AbstractMvcView implements View {
this.fieldMarkerPrefix = fieldMarkerPrefix;
}
+ /**
+ * Sets the name of the request parameter to use to lookup user events signaled by this view. If not specified, the
+ * default is _eventId
+ * @param eventIdParameterName the event id parameter name
+ */
+ public void setEventIdParameterName(String eventIdParameterName) {
+ this.eventIdParameterName = eventIdParameterName;
+ }
+
public void render() throws IOException {
Map model = new HashMap();
model.putAll(flowScopes());
@@ -308,10 +319,7 @@ public abstract class AbstractMvcView implements View {
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());
- }
+ mapping.setRequired(binding.getRequired());
if (binding.getConverter() != null) {
ConversionExecutor conversionExecutor = conversionService.getConversionExecutor(binding.getConverter(),
String.class, target.getValueType(model));
@@ -441,7 +449,7 @@ public abstract class AbstractMvcView implements View {
}
private void determineEventId(RequestContext context) {
- eventId = WebUtils.findParameterValue(context.getRequestParameters().asMap(), "_eventId");
+ eventId = WebUtils.findParameterValue(context.getRequestParameters().asMap(), eventIdParameterName);
}
private static class PropertyNotFoundError implements MappingResultsCriteria {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcViewFactory.java
index 23d2dd8d..9753c0f4 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcViewFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcViewFactory.java
@@ -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.util.StringUtils;
import org.springframework.webflow.engine.model.BinderModel;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.View;
@@ -40,6 +41,10 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
private BinderModel binderModel;
+ private String eventIdParameterName;
+
+ private String fieldMarkerPrefix;
+
public AbstractMvcViewFactory(Expression viewId, FlowViewResolver viewResolver, ExpressionParser expressionParser,
ConversionService conversionService, BinderModel binderModel) {
this.viewId = viewId;
@@ -49,22 +54,28 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
this.binderModel = binderModel;
}
- protected ExpressionParser getExpressionParser() {
- return expressionParser;
+ public void setEventIdParameterName(String eventIdParameterName) {
+ this.eventIdParameterName = eventIdParameterName;
}
- protected ConversionService getConversionService() {
- return conversionService;
- }
-
- protected BinderModel getBinderModel() {
- return binderModel;
+ public void setFieldMarkerPrefix(String fieldMarkerPrefix) {
+ this.fieldMarkerPrefix = fieldMarkerPrefix;
}
public View getView(RequestContext context) {
String viewId = (String) this.viewId.getValue(context);
org.springframework.web.servlet.View view = viewResolver.resolveView(viewId, context);
- return createMvcView(view, context);
+ AbstractMvcView mvcView = createMvcView(view, context);
+ mvcView.setExpressionParser(expressionParser);
+ mvcView.setConversionService(conversionService);
+ mvcView.setBinderModel(binderModel);
+ if (StringUtils.hasText(eventIdParameterName)) {
+ mvcView.setEventIdParameterName(eventIdParameterName);
+ }
+ if (StringUtils.hasText(fieldMarkerPrefix)) {
+ mvcView.setFieldMarkerPrefix(fieldMarkerPrefix);
+ }
+ return mvcView;
}
protected abstract AbstractMvcView createMvcView(org.springframework.web.servlet.View view, RequestContext context);
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java
index 8df7399a..49069e81 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java
@@ -216,8 +216,7 @@ public class MvcViewTests extends TestCase {
org.springframework.web.servlet.View mvcView = new MockView();
AbstractMvcView view = new MockMvcView(mvcView, context);
BinderModel binderModel = new BinderModel();
- binderModel
- .addBinding(new org.springframework.webflow.engine.model.BindingModel("stringProperty", null, "true"));
+ binderModel.addBinding(new org.springframework.webflow.engine.model.BindingModel("stringProperty", null, true));
view.setBinderModel(binderModel);
view.processUserEvent();
assertTrue(view.hasFlowEvent());