/* * 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; } } } }