/* * Copyright 2002-2017 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 * * https://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.validation; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.springframework.util.StringUtils; /** * Default implementation of the {@link MessageCodesResolver} interface. * *
Will create two message codes for an object error, in the following order (when * using the {@link Format#PREFIX_ERROR_CODE prefixed} * {@link #setMessageCodeFormatter(MessageCodeFormatter) formatter}): *
Will create four message codes for a field specification, in the following order: *
For example, in case of code "typeMismatch", object name "user", field "age": *
This resolution algorithm thus can be leveraged for example to show * specific messages for binding errors like "required" and "typeMismatch": *
In case of array, {@link List} or {@link java.util.Map} properties, * both codes for specific elements and for the whole collection are * generated. Assuming a field "name" of an array "groups" in object "user": *
By default the {@code errorCode}s will be placed at the beginning of constructed * message strings. The {@link #setMessageCodeFormatter(MessageCodeFormatter) * messageCodeFormatter} property can be used to specify an alternative concatenation * {@link MessageCodeFormatter format}. * *
In order to group all codes into a specific category within your resource bundles, * e.g. "validation.typeMismatch.name" instead of the default "typeMismatch.name", * consider specifying a {@link #setPrefix prefix} to be applied. * * @author Juergen Hoeller * @author Phillip Webb * @author Chris Beams * @since 1.0.1 */ @SuppressWarnings("serial") public class DefaultMessageCodesResolver implements MessageCodesResolver, Serializable { /** * The separator that this implementation uses when resolving message codes. */ public static final String CODE_SEPARATOR = "."; private static final MessageCodeFormatter DEFAULT_FORMATTER = Format.PREFIX_ERROR_CODE; private String prefix = ""; private MessageCodeFormatter formatter = DEFAULT_FORMATTER; /** * Specify a prefix to be applied to any code built by this resolver. *
Default is none. Specify, for example, "validation." to get * error codes like "validation.typeMismatch.name". */ public void setPrefix(String prefix) { this.prefix = (prefix != null ? prefix : ""); } /** * Return the prefix to be applied to any code built by this resolver. *
Returns an empty String in case of no prefix. */ protected String getPrefix() { return this.prefix; } /** * Specify the format for message codes built by this resolver. *
The default is {@link Format#PREFIX_ERROR_CODE}. * @since 3.2 * @see Format */ public void setMessageCodeFormatter(MessageCodeFormatter formatter) { this.formatter = (formatter != null ? formatter : DEFAULT_FORMATTER); } @Override public String[] resolveMessageCodes(String errorCode, String objectName) { return resolveMessageCodes(errorCode, objectName, "", null); } /** * Build the code list for the given code and field: an * object/field-specific code, a field-specific code, a plain error code. *
Arrays, Lists and Maps are resolved both for specific elements and * the whole collection. *
See the {@link DefaultMessageCodesResolver class level javadoc} for
* details on the generated codes.
* @return the list of codes
*/
@Override
public String[] resolveMessageCodes(String errorCode, String objectName, String field, Class> fieldType) {
Set The default implementation applies the specified prefix, if any.
* @param code the message code as built by this resolver
* @return the final message code to be returned
* @see #setPrefix
*/
protected String postProcessMessageCode(String code) {
return getPrefix() + code;
}
/**
* Common message code formats.
* @see MessageCodeFormatter
* @see DefaultMessageCodesResolver#setMessageCodeFormatter(MessageCodeFormatter)
*/
public enum Format implements MessageCodeFormatter {
/**
* Prefix the error code at the beginning of the generated message code. e.g.:
* {@code errorCode + "." + object name + "." + field}
*/
PREFIX_ERROR_CODE {
@Override
public String format(String errorCode, String objectName, String field) {
return toDelimitedString(errorCode, objectName, field);
}
},
/**
* Postfix the error code at the end of the generated message code. e.g.:
* {@code object name + "." + field + "." + errorCode}
*/
POSTFIX_ERROR_CODE {
@Override
public String format(String errorCode, String objectName, String field) {
return toDelimitedString(objectName, field, errorCode);
}
};
/**
* Concatenate the given elements, delimiting each with
* {@link DefaultMessageCodesResolver#CODE_SEPARATOR}, skipping zero-length or
* null elements altogether.
*/
public static String toDelimitedString(String... elements) {
StringBuilder rtn = new StringBuilder();
for (String element : elements) {
if (StringUtils.hasLength(element)) {
rtn.append(rtn.length() == 0 ? "" : CODE_SEPARATOR);
rtn.append(element);
}
}
return rtn.toString();
}
}
}