made event id configurable; made requird a boolean

This commit is contained in:
Keith Donald
2008-07-27 04:10:54 +00:00
parent b70c76d459
commit 808bdabe6f
8 changed files with 109 additions and 78 deletions

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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 <code>_eventId</code>
* @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.
* <p>
* Default is "_", for "_FIELD" parameters (e.g. "_subscribeToNewsletter"). Set this to null if you want to turn off
* the empty field check completely.
* <p>
* 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.
* <p>
* 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) {

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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 <code>_eventId</code>
* @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 {

View File

@@ -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);

View File

@@ -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());