/*
* Copyright 2004-2012 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.binding.message;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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.util.StringUtils;
import org.springframework.validation.AbstractErrors;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.ObjectError;
/**
* Adapts a MessageContext object to the Spring Errors interface. Allows Spring Validators to record errors that are
* managed by a backing MessageContext.
*
* @author Keith Donald
*/
public class MessageContextErrors extends AbstractErrors {
private MessageContext messageContext;
private String objectName;
private Object boundObject;
private ExpressionParser expressionParser;
private MappingResults mappingResults;
private MessageCodesResolver bindingErrorMessageCodesResolver;
/**
* Creates a new message context errors adapter.
* @param messageContext the backing message context
* @param objectName the object name
* @param boundObject the model object
* @param expressionParser the expression parser
* @param bindingErrorMessageCodesResolver the message codes resolver
* @param mappingResults object mapping results
*/
public MessageContextErrors(MessageContext messageContext, String objectName, Object boundObject,
ExpressionParser expressionParser, MessageCodesResolver bindingErrorMessageCodesResolver,
MappingResults mappingResults) {
this.messageContext = messageContext;
this.objectName = objectName;
this.boundObject = boundObject;
this.expressionParser = expressionParser;
this.bindingErrorMessageCodesResolver = bindingErrorMessageCodesResolver;
this.mappingResults = mappingResults;
}
public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
String[] messageCodes = bindingErrorMessageCodesResolver.resolveMessageCodes(errorCode, objectName);
messageContext.addMessage(new MessageBuilder().error().codes(messageCodes).args(errorArgs)
.defaultText(defaultMessage).build());
}
public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
field = fixedField(field);
Class<?> fieldType;
if (StringUtils.hasLength(field) && (expressionParser != null)) {
FluentParserContext parserContext = new FluentParserContext().evaluate(boundObject.getClass());
fieldType = expressionParser.parseExpression(field, parserContext).getValueType(boundObject);
} else {
fieldType = null;
}
String[] messageCodes;
if (StringUtils.hasLength(field)) {
messageCodes = bindingErrorMessageCodesResolver
.resolveMessageCodes(errorCode, objectName, field, fieldType);
} else {
messageCodes = bindingErrorMessageCodesResolver.resolveMessageCodes(errorCode, objectName);
}
messageContext.addMessage(new MessageBuilder().error().source(field).codes(messageCodes).args(errorArgs)
.defaultText(defaultMessage).build());
}
public void addAllErrors(Errors errors) {
for (ObjectError error : errors.getAllErrors()) {
MessageBuilder builder = new MessageBuilder().error().codes(error.getCodes()).args(error.getArguments())
.defaultText(error.getDefaultMessage());
if (error instanceof FieldError) {
FieldError fieldError = (FieldError) error;
builder.source(fieldError.getField());
}
messageContext.addMessage(builder.build());
}
}
public String getObjectName() {
return objectName;
}
public List<ObjectError> getGlobalErrors() {
Message[] messages = messageContext.getMessagesByCriteria(GLOBAL_ERROR);
if (messages.length == 0) {
return Collections.emptyList();
}
List<ObjectError> errors = new ArrayList<ObjectError>(messages.length);
for (Message message : messages) {
errors.add(new ObjectError(objectName, message.getText()));
}
return Collections.unmodifiableList(errors);
}
public List<FieldError> getFieldErrors() {
Message[] messages = messageContext.getMessagesByCriteria(FIELD_ERROR);
if (messages.length == 0) {
return Collections.emptyList();
}
List<FieldError> errors = new ArrayList<FieldError>(messages.length);
for (Message message : messages) {
errors.add(new FieldError(objectName, (String) message.getSource(), message.getText()));
}
return Collections.unmodifiableList(errors);
}
public Object getFieldValue(String field) {
field = fixedField(field);
// requires boundObject and expressionParser to be set to work
if (mappingResults != null) {
List<MappingResult> results = mappingResults.getResults(new PropertyErrorMappingResult(field));
if (!results.isEmpty()) {
MappingResult fieldError = results.get(0);
return fieldError.getOriginalValue();
}
}
return parseFieldExpression(field).getValue(boundObject);
}
// internal helpers
private Expression parseFieldExpression(String field) {
return expressionParser.parseExpression(field, new FluentParserContext().evaluate(boundObject.getClass()));
}
private static MessageCriteria GLOBAL_ERROR = new MessageCriteria() {
public boolean test(Message message) {
if (message.getSeverity() == Severity.ERROR && message.getSource() == null) {
return true;
} else {
return false;
}
}
};
private static MessageCriteria FIELD_ERROR = new MessageCriteria() {
public boolean test(Message message) {
if (message.getSeverity() == Severity.ERROR && message.getSource() instanceof String) {
return true;
} else {
return false;
}
}
};
private static class PropertyErrorMappingResult implements MappingResultsCriteria {
private String property;
public PropertyErrorMappingResult(String property) {
this.property = property;
}
public boolean test(MappingResult result) {
if (result.isError() && property.equals(result.getMapping().getTargetExpression().getExpressionString())) {
return true;
} else {
return false;
}
}
}
}