All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.validation.DefaultMessageCodesResolver Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * 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 java.util.StringJoiner;

import org.springframework.lang.Nullable;
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}): *

    *
  • 1.: code + "." + object name *
  • 2.: code *
* *

Will create four message codes for a field specification, in the following order: *

    *
  • 1.: code + "." + object name + "." + field *
  • 2.: code + "." + field *
  • 3.: code + "." + field type *
  • 4.: code *
* *

For example, in case of code "typeMismatch", object name "user", field "age": *

    *
  • 1. try "typeMismatch.user.age" *
  • 2. try "typeMismatch.age" *
  • 3. try "typeMismatch.int" *
  • 4. try "typeMismatch" *
* *

This resolution algorithm thus can be leveraged for example to show * specific messages for binding errors like "required" and "typeMismatch": *

    *
  • at the object + field level ("age" field, but only on "user"); *
  • at the field level (all "age" fields, no matter which object name); *
  • or at the general level (all fields, on any object). *
* *

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": *

    *
  • 1. try "typeMismatch.user.groups[0].name" *
  • 2. try "typeMismatch.user.groups.name" *
  • 3. try "typeMismatch.groups[0].name" *
  • 4. try "typeMismatch.groups.name" *
  • 5. try "typeMismatch.name" *
  • 6. try "typeMismatch.java.lang.String" *
  • 7. try "typeMismatch" *
* *

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(@Nullable 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(@Nullable 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, @Nullable Class fieldType) { Set codeList = new LinkedHashSet<>(); List fieldList = new ArrayList<>(); buildFieldList(field, fieldList); addCodes(codeList, errorCode, objectName, fieldList); int dotIndex = field.lastIndexOf('.'); if (dotIndex != -1) { buildFieldList(field.substring(dotIndex + 1), fieldList); } addCodes(codeList, errorCode, null, fieldList); if (fieldType != null) { addCode(codeList, errorCode, null, fieldType.getName()); } addCode(codeList, errorCode, null, null); return StringUtils.toStringArray(codeList); } private void addCodes(Collection codeList, String errorCode, @Nullable String objectName, Iterable fields) { for (String field : fields) { addCode(codeList, errorCode, objectName, field); } } private void addCode(Collection codeList, String errorCode, @Nullable String objectName, @Nullable String field) { codeList.add(postProcessMessageCode(this.formatter.format(errorCode, objectName, field))); } /** * Add both keyed and non-keyed entries for the supplied {@code field} * to the supplied field list. */ protected void buildFieldList(String field, List fieldList) { fieldList.add(field); String plainField = field; int keyIndex = plainField.lastIndexOf('['); while (keyIndex != -1) { int endKeyIndex = plainField.indexOf(']', keyIndex); if (endKeyIndex != -1) { plainField = plainField.substring(0, keyIndex) + plainField.substring(endKeyIndex + 1); fieldList.add(plainField); keyIndex = plainField.lastIndexOf('['); } else { keyIndex = -1; } } } /** * Post-process the given message code, built by this resolver. *

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, @Nullable String objectName, @Nullable 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, @Nullable String objectName, @Nullable 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) { StringJoiner rtn = new StringJoiner(CODE_SEPARATOR); for (String element : elements) { if (StringUtils.hasLength(element)) { rtn.add(element); } } return rtn.toString(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy