[[chapter-message-interpolation]]
== Interpolating constraint error messages

Message interpolation is the process of creating error messages for violated Bean Validation
constraints. In this chapter you will learn how such messages are defined and resolved and how you
can plug in custom message interpolators in case the default algorithm is not sufficient for your
requirements.

[[section-message-interpolation]]
=== Default message interpolation

Constraint violation messages are retrieved from so called message descriptors. Each constraint
defines its default message descriptor using the message attribute. At declaration time, the default
descriptor can be overridden with a specific value as shown in <<example-overriding-message>>.

[[example-overriding-message]]
.Specifying a message descriptor using the message attribute
====
[source, JAVA]
----
package org.hibernate.validator.referenceguide.chapter04;

public class Car {

	@NotNull(message = "The manufacturer name must not be null")
	private String manufacturer;

	//constructor, getters and setters ...
}
----
====

If a constraint is violated, its descriptor will be interpolated by the validation engine using the
currently configured `MessageInterpolator`. The interpolated error message can then be retrieved from
the resulting constraint violation by calling `ConstraintViolation#getMessage()`.

Message descriptors can contain _message parameters_ as well as _message expressions_ which will be
resolved during interpolation. Message parameters are string literals enclosed in `{}`, while
message expressions are string literals enclosed in `${}`. The following algorithm is applied during
method interpolation:


. Resolve any message parameters by using them as key for the resource bundle _ValidationMessages_. If
this bundle contains an entry for a given message parameter, that parameter will be replaced in the
message with the corresponding value from the bundle. This step will be executed recursively in case
the replaced value again contains message parameters. The resource bundle is expected to be provided
by the application developer, e.g. by adding a file named _ValidationMessages.properties_ to the
classpath. You can also create localized error messages by providing locale specific variations of
this bundle, such as _$$ValidationMessages_en_US.properties$$_. By default, the JVM's default locale
(`Locale#getDefault()`) will be used when looking up messages in the bundle.

. Resolve any message parameters by using them as key for a resource bundle containing the standard
error messages for the built-in constraints as defined in Appendix B of the Bean Validation
specification. In the case of Hibernate Validator, this bundle is named
`org.hibernate.validator.ValidationMessages`. If this step triggers a replacement, step 1 is executed
again, otherwise step 3 is applied.


. Resolve any message parameters by replacing them with the value of the constraint annotation member
of the same name. This allows to refer to attribute values of the constraint (e.g. `Size#min()`) in
the error message (e.g. "must be at least ${min}").


. Resolve any message expressions by evaluating them as expressions of the Unified Expression
Language. See <<section-interpolation-with-message-expressions>> to learn more about the usage of
Unified EL in error messages.

[TIP]
====
You can find the formal definition of the interpolation algorithm in section
link:http://beanvalidation.org/1.1/spec/#default-resolution-algorithm[5.3.1.1] of the Bean
Validation specification.
====

[[section-special-characters]]
==== Special characters

Since the characters `{`, `}` and `$` have a special meaning in message descriptors they need to be escaped if you want to use them literally. The following rules apply:

* `\{` is considered as the literal `{`
* `\}` is considered as the literal `}`
* `\$` is considered as the literal `$`
* `\\` is considered as the literal `\`


[[section-interpolation-with-message-expressions]]
==== Interpolation with message expressions

As of Hibernate Validator 5 (Bean Validation 1.1) it is possible to use the Unified Expression
Language (as defined by link:http://jcp.org/en/jsr/detail?id=341[JSR 341]) in constraint
violation messages. This allows to define error messages based on conditional logic and also enables
advanced formatting options. The validation engine makes the following objects available in the EL
context:

* the attribute values of the constraint mapped to the attribute names
* the currently validated value (property, bean, method parameter etc.) under the name _validatedValue_
* a bean mapped to the name formatter exposing the var-arg method
`format(String format, Object... args)` which behaves like
`java.util.Formatter.format(String format, Object... args)`.

The following section provides several examples for using EL expressions in error messages.

==== Examples

<<example-message-descriptors>> shows how to make use of the different options for specifying
message descriptors.

[[example-message-descriptors]]
.Specifying message descriptors
====
[source, JAVA]
----
package org.hibernate.validator.referenceguide.chapter04.complete;

public class Car {

	@NotNull
	private String manufacturer;

	@Size(min = 2,
		max = 14,
		message = "The license plate '${validatedValue}' must be between {min} and {max} characters long"
	)
	private String licensePlate;

	@Min(value = 2,
		message = "There must be at least {value} seat${value > 1 ? 's' : ''}"
	)
	private int seatCount;

	@DecimalMax(value = "350",
			message = "The top speed ${formatter.format('%1$.2f', validatedValue)} is higher " +
					"than {value}"
	)
	private double topSpeed;

	@DecimalMax(value = "100000", message = "Price must not be higher than ${value}")
	private BigDecimal price;

	public Car(
			String manufacturer,
			String licensePlate,
			int seatCount,
			double topSpeed,
			BigDecimal price) {
		this.manufacturer = manufacturer;
		this.licensePlate = licensePlate;
		this.seatCount = seatCount;
		this.topSpeed = topSpeed;
		this.price = price;
	}

	//getters and setters ...
}
----
====

Validating an invalid `Car` instance yields constraint violations with the messages shown by the
assertions in <<example-expected-error-messages>>:

* the `@NotNull` constraint on the `manufacturer` field causes the error message "may not be null", as
this is the default message defined by the Bean Validation specification and no specific descriptor
is given in the message attribute

* the `@Size` constraint on the `licensePlate` field shows the interpolation of message parameters
(`{min}`, `{max}`) and how to add the validated value to the error message using the EL
expression `${validatedValue}`

* the `@Min` constraint on `seatCount` demonstrates how use an EL expression with a ternery expression to
dynamically chose singular or plural form, depending on an attribute of the constraint ("There must
be at least 1 seat" vs. "There must be at least 2 seats")

* the message for the `@DecimalMax` constraint on `topSpeed` shows how to format the validated
value using the formatter instance

* finally, the `@DecimalMax` constraint on price shows that parameter interpolation has precedence over
expression evaluation, causing the `$` sign to show up in front of the maximum price

[TIP]
====
Only actual constraint attributes can be interpolated using message parameters in the form
`{attributeName}`. When referring to the validated value or custom expression variables added to the
interpolation context (see <<section-hibernateconstraintvalidatorcontext>>), an EL expression in the
form `${attributeName}` must be used.
====

[[example-expected-error-messages]]
.Expected error messages
====
[source, JAVA]
----
Car car = new Car( null, "A", 1, 400.123456, BigDecimal.valueOf( 200000 ) );

String message = validator.validateProperty( car, "manufacturer" )
		.iterator()
		.next()
		.getMessage();
assertEquals( "may not be null", message );

message = validator.validateProperty( car, "licensePlate" )
		.iterator()
		.next()
		.getMessage();
assertEquals(
		"The license plate must be between 2 and 14 characters long",
		message
);

message = validator.validateProperty( car, "seatCount" ).iterator().next().getMessage();
assertEquals( "There must be at least 2 seats", message );

message = validator.validateProperty( car, "topSpeed" ).iterator().next().getMessage();
assertEquals( "The top speed 400.12 is higher than 350", message );

message = validator.validateProperty( car, "price" ).iterator().next().getMessage();
assertEquals( "Price must not be higher than $100000", message );
----
====

[[section-custom-message-interpolation]]
=== Custom message interpolation

If the default message interpolation algorithm does not fit your requirements it is also possible to
plug in a custom `MessageInterpolator` implementation.

Custom interpolators must implement the interface `javax.validation.MessageInterpolator`. Note that
implementations must be thread-safe. It is recommended that custom message interpolators delegate
final implementation to the default interpolator, which can be obtained via
`Configuration#getDefaultMessageInterpolator()`.

In order to use a custom message interpolator it must be registered either by configuring it in the
Bean Validation XML descriptor _META-INF/validation.xml_ (see
<<section-configuration-validation-xml>>) or by passing it when bootstrapping a `ValidatorFactory` or
`Validator` (see <<section-validator-factory-message-interpolator>> and
<<section-configuring-validator>>, respectively).

[[section-resource-bundle-locator]]
==== `ResourceBundleLocator`

In some use cases you want to use the message interpolation algorithm as defined by the Bean
Validation specification, but retrieve error messages from other resource bundles than
_ValidationMessages_. In this situation Hibernate Validator's `ResourceBundleLocator` SPI can help.

The default message interpolator in Hibernate Validator, `ResourceBundleMessageInterpolator`,
delegates retrieval of resource bundles to that SPI. Using an alternative bundle only requires
passing an instance of `PlatformResourceBundleLocator` with the bundle name when bootstrapping the
`ValidatorFactory` as shown in <<example-using-specific-resource-bundle-locator>>.

[[example-using-specific-resource-bundle-locator]]
.Using a specific resource bundle
====
[source, JAVA]
----
Validator validator = Validation.byDefaultProvider()
		.configure()
		.messageInterpolator(
				new ResourceBundleMessageInterpolator(
						new PlatformResourceBundleLocator( "MyMessages" )
				)
		)
		.buildValidatorFactory()
		.getValidator();
----
====

Of course you also could implement a completely different `ResourceBundleLocator`, which for instance
returns bundles backed by records in a database. In this case you can obtain the default locator via
`HibernateValidatorConfiguration#getDefaultResourceBundleLocator()`, which you e.g. could use as
fall-back for your custom locator.

Besides `PlatformResourceBundleLocator`, Hibernate Validator provides another resource bundle locator
implementation out of the box, namely `AggregateResourceBundleLocator`, which allows to retrieve error
messages from more than one resource bundle. You could for instance use this implementation in a
multi-module application where you want to have one message bundle per module.
<<example-using-aggregate-resource-bundle-locator>> shows how to use `AggregateResourceBundleLocator`.

[[example-using-aggregate-resource-bundle-locator]]
.Using `AggregateResourceBundleLocator`
====
[source, JAVA]
----
Validator validator = Validation.byDefaultProvider()
		.configure()
		.messageInterpolator(
				new ResourceBundleMessageInterpolator(
						new AggregateResourceBundleLocator(
								Arrays.asList(
										"MyMessages",
										"MyOtherMessages"
								)
						)
				)
		)
		.buildValidatorFactory()
		.getValidator();
----
====

Note that the bundles are processed in the order as passed to the constructor. That means if several
bundles contain an entry for a given message key, the value will be taken from the first bundle in
the list containing the key.

