org.zkoss.spring.webflow.execution.ZkFlowView Maven / Gradle / Ivy
/* ZkFlowView.java
{{IS_NOTE
Purpose:
Description:
History:
Nov 12, 2008 13:01:13 PM, Created by henrichen
}}IS_NOTE
Copyright (C) 2008 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under GPL Version 2.0 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.spring.webflow.execution;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.expression.support.FluentParserContext;
import org.springframework.binding.mapping.MappingResult;
import org.springframework.binding.mapping.MappingResults;
import org.springframework.binding.mapping.MappingResultsCriteria;
import org.springframework.binding.mapping.impl.DefaultMapper;
import org.springframework.binding.mapping.impl.DefaultMapping;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.message.MessageContextErrors;
import org.springframework.binding.message.MessageResolver;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.web.servlet.View;
import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.mvc.servlet.ServletMvcView;
import org.springframework.webflow.validation.WebFlowMessageCodesResolver;
import org.zkoss.spring.webflow.context.servlet.ZkFlowContextManager;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zkplus.databind.Binding;
import org.zkoss.zkplus.databind.DataBinder;
/**
* ZK implementation for {@link org.springframework.webflow.execution.View}. Resolve ZK event to Spring Web Flow event.
*
* @author henrichen
* @since 1.1
*/
public class ZkFlowView extends ServletMvcView {
private static final Log logger = LogFactory.getLog(ZkFlowView.class);
private static final MappingResultsCriteria PROPERTY_NOT_FOUND_ERROR = new PropertyNotFoundError();
private static final MappingResultsCriteria MAPPING_ERROR = new MappingError();
private String eventId;
private boolean viewErrors;
private RequestContext requestContext;
private ExpressionParser expressionParser;
private MappingResults mappingResults;
private final MessageCodesResolver messageCodeResolver = new WebFlowMessageCodesResolver();
public ZkFlowView(View view, RequestContext context) {
super(view, context);
requestContext = context;
}
public void setExpressionParser(ExpressionParser expressionParser) {
super.setExpressionParser(expressionParser);
this.expressionParser = expressionParser;
}
public void processUserEvent() {
determineEventId(requestContext);
if (eventId == null) {
return;
}
TransitionDefinition transition = requestContext.getMatchingTransition(eventId);
if (shouldBind(transition)) {
mappingResults = bind();
if (mappingResults == null) {
return;
}
if (hasMappingErrors(mappingResults)) {
viewErrors = true;
addErrorMessages(mappingResults);
} else {
if (shouldValidate(transition)) {
//model is used for validation only!
Object model = getModelObject();
if (model == null) {
return;
}
validate(model);
if (requestContext.getMessageContext().hasErrorMessages()) {
viewErrors = true;
}
}
}
}
}
public boolean hasFlowEvent() {
return eventId != null && !viewErrors;
}
public Event getFlowEvent() {
if (!hasFlowEvent()) {
return null;
}
return new Event(this, eventId, requestContext.getRequestParameters().asAttributeMap());
}
protected String determineEventId(RequestContext context) {
eventId = context.getExternalContext().getRequestMap().getString("action");
return eventId;
}
private MappingResults bind() {
final Execution exec = Executions.getCurrent();
final Component self = ZkFlowContextManager.getSelf(exec);
if (self != null) {
final DataBinder binder = (DataBinder) self.getVariable("binder", false);
if (binder != null) {
final Collection bindings = binder.getAllBindings();
if (!bindings.isEmpty()) {
final RequestContext srcRequestContext =
ZkFlowContextManager.getFlowRequestContext(exec);
if (srcRequestContext != null) {
if (logger.isDebugEnabled()) {
logger.debug("Setting up view->model mappings");
}
DefaultMapper mapper = new DefaultMapper();
if (addModelBindingMappings(mapper, bindings, srcRequestContext, requestContext)) {
return mapper.map(srcRequestContext, requestContext);
}
}
}
}
}
return null;
}
private boolean addModelBindingMappings(DefaultMapper mapper, Collection bindings, RequestContext src, RequestContext target) {
boolean everMapping = false;
for(final Iterator it = bindings.iterator(); it.hasNext();) {
final Binding binding = (Binding)it.next();
if (binding.isSavable()) {
final String expr = (String) binding.getExpression();
addMapping(mapper, expr);
everMapping = true;
}
}
return everMapping;
}
private void addMapping(DefaultMapper mapper, String expressionString) {
Expression expression = expressionParser.parseExpression(expressionString,
new FluentParserContext().evaluate(RequestContext.class));
DefaultMapping mapping = new DefaultMapping(expression, expression);
mapping.setRequired(false);
if (logger.isDebugEnabled()) {
logger.debug("Adding mapping for expression '" + expressionString + "'");
}
mapper.addMapping(mapping);
}
private boolean shouldBind(TransitionDefinition transition) {
if (transition == null) {
return true;
}
return transition.getAttributes().getBoolean("bind", Boolean.TRUE).booleanValue();
}
private boolean hasMappingErrors(MappingResults results) {
return results.hasErrorResults() && !onlyPropertyNotFoundErrorsPresent(results);
}
private boolean onlyPropertyNotFoundErrorsPresent(MappingResults results) {
return results.getResults(PROPERTY_NOT_FOUND_ERROR).size() == mappingResults.getErrorResults().size();
}
private void addErrorMessages(MappingResults results) {
List errors = results.getResults(MAPPING_ERROR);
for (Iterator it = errors.iterator(); it.hasNext();) {
MappingResult error = (MappingResult) it.next();
requestContext.getMessageContext().addMessage(createMessageResolver(error));
}
}
private MessageResolver createMessageResolver(MappingResult error) {
String field = error.getMapping().getTargetExpression().getExpressionString();
String errorCode = error.getCode();
String propertyErrorCode = new StringBuffer().append(getModelExpression().getExpressionString()).append('.')
.append(field).append('.').append(errorCode).toString();
return new MessageBuilder().error().source(field).code(propertyErrorCode).code(errorCode).resolvableArg(field)
.defaultText(errorCode + " on " + field).build();
}
private boolean shouldValidate(TransitionDefinition transition) {
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,
new Class[] { MessageContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { requestContext.getMessageContext() });
}
BeanFactory beanFactory = requestContext.getActiveFlow().getApplicationContext();
if (beanFactory != null) {
String validatorName = getModelExpression().getExpressionString() + "Validator";
if (beanFactory.containsBean(validatorName)) {
Object validator = beanFactory.getBean(validatorName);
validateMethod = ReflectionUtils.findMethod(validator.getClass(), validateMethodName, new Class[] {
model.getClass(), MessageContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
requestContext.getMessageContext() });
} else {
validateMethod = ReflectionUtils.findMethod(validator.getClass(), validateMethodName, new Class[] {
model.getClass(), Errors.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
new MessageContextErrors(requestContext.getMessageContext(), validatorName, validator, expressionParser, messageCodeResolver, mappingResults) });
}
}
}
}
}
private Object getModelObject() {
Expression model = getModelExpression();
if (model != null) {
return model.getValue(requestContext);
} else {
return null;
}
}
private Expression getModelExpression() {
return (Expression) requestContext.getCurrentState().getAttributes().get("model");
}
private static class PropertyNotFoundError implements MappingResultsCriteria {
public boolean test(MappingResult result) {
return result.isError() && "propertyNotFound".equals(result.getCode());
}
}
private static class MappingError implements MappingResultsCriteria {
public boolean test(MappingResult result) {
return result.isError() && !PROPERTY_NOT_FOUND_ERROR.test(result);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy