<?xml version="1.0" encoding="UTF-8"?>
<!--
	~ JBoss, Home of Professional Open Source
	~ Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual contributors
	~ by the @authors tag. See the copyright.txt in the distribution for a
	~ full listing of individual contributors.
	~
	~ 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.
-->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../hv.ent">
%BOOK_ENTITIES;
]>
<chapter id="chapter-message-interpolation">
  <title>Interpolating constraint error messages</title>

  <para>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.</para>

  <section id="section-message-interpolation">
    <title>Default message interpolation</title>

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

    <example id="example-overriding-message">
      <title>Specifying a message descriptor using the
      <methodname>message</methodname> attribute</title>

      <programlisting language="JAVA" role="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 ...
}</programlisting>
    </example>

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

    <para>Message descriptors can contain <emphasis>message
    parameters</emphasis> as well as <emphasis>message expressions</emphasis>
    which will be resolved during interpolation. Message parameters are string
    literals enclosed in <literal>{}</literal>, while message expressions are
    string literals enclosed in <literal>${}</literal>. The following
    algorithm is applied during method interpolation:</para>

    <orderedlist>
      <listitem>
        <para>Resolve any message parameters by using them as key for the
        resource bundle <filename>ValidationMessages</filename>. 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 <filename>ValidationMessages.properties</filename> to the
        classpath. You can also create localized error messages by providing
        locale specific variations of this bundle, such as
        <filename>ValidationMessages_en_US.properties</filename>. By default,
        the JVM's default locale
        (<methodname>Locale#getDefault()</methodname>) will be used when
        looking up messages in the bundle.</para>
      </listitem>

      <listitem>
        <para>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
        <classname>org.hibernate.validator.ValidationMessages</classname>. If
        this step triggers a replacement, step 1 is executed again, otherwise
        step 3 is applied.</para>
      </listitem>

      <listitem>
        <para>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.
        <methodname>Size#min()</methodname>) in the error message (e.g. "must
        be at least ${min}").</para>
      </listitem>

      <listitem>
        <para>Resolve any message expressions by evaluating them as
        expressions of the Unified Expression Language. See <xref
        linkend="section-interpolation-with-message-expressions"/> to learn
        more about the usage of Unified EL in error messages.</para>
      </listitem>
    </orderedlist>

    <tip>
      <para>You can find the formal definition of the interpolation algorithm
      in section <ulink
      url="http://beanvalidation.org/1.1/spec/#default-resolution-algorithm">5.3.1.1</ulink>
      of the Bean Validation specification.</para>
    </tip>

    <section id="section-special-characters">
      <title>Special characters</title>

      <para>Since the characters <literal>{</literal>, <literal>}</literal>
      and <literal>$</literal> have a special meaning in message descriptors
      they need to be escaped if you want to use them literally. The following
      rules apply:<itemizedlist>
          <listitem>
            <para><literal><literal>\{</literal></literal> is considered as
            the literal <literal>{</literal></para>
          </listitem>

          <listitem>
            <para><literal>\}</literal> is considered as the literal
            <literal>}</literal></para>
          </listitem>

          <listitem>
            <para><literal>\$</literal> is considered as the literal
            <literal>$</literal></para>
          </listitem>

          <listitem>
            <para><literal>\\</literal> is considered as the literal
            <literal>\</literal></para>
          </listitem>
        </itemizedlist></para>
    </section>

    <section id="section-interpolation-with-message-expressions">
      <title>Interpolation with message expressions</title>

      <para>As of Hibernate Validator 5 (Bean Validation 1.1) it is possible
      to use the Unified Expression Language (as defined by <ulink
      url="http://jcp.org/en/jsr/detail?id=341">JSR 341</ulink>) 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:</para>

      <itemizedlist>
        <listitem>
          <para>the attribute values of the constraint mapped to the attribute
          names</para>
        </listitem>

        <listitem>
          <para>the currently validated value (property, bean, method
          parameter etc.) under the name
          <varname>validatedValue</varname></para>
        </listitem>

        <listitem>
          <para>a bean mapped to the name <varname>formatter</varname>
          exposing the var-arg method <methodname>format(String format,
          Object... args)</methodname> which behaves like
          <methodname>java.util.Formatter.format(String format, Object...
          args)</methodname>.</para>
        </listitem>
      </itemizedlist>

      <para>The following section provides several examples for using EL
      expressions in error messages.</para>
    </section>

    <section>
      <title>Examples</title>

      <para><xref linkend="example-message-descriptors"/> shows how to make
      use of the different options for specifying message descriptors.</para>

      <example id="example-message-descriptors">
        <title>Specifying message descriptors</title>

        <programlisting language="JAVA" role="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 &gt; 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 ...
}</programlisting>
      </example>

      <para>Validating an invalid <classname>Car</classname> instance yields
      constraint violations with the messages shown by the assertions in <xref
      linkend="example-expected-error-messages"/>:</para>

      <itemizedlist>
        <listitem>
          <para>the <classname>@NotNull</classname> constraint on the
          <varname>manufacturer</varname> 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
          <methodname>message</methodname> attribute</para>
        </listitem>

        <listitem>
          <para>the <classname>@Size</classname> constraint on the
          <varname>licensePlate</varname> field shows the interpolation of
          message parameters (<literal>{min}</literal>,
          <literal>{max}</literal>) and how to add the validated value to the
          error message using the EL expression <literal>${validatedValue}</literal>
          </para>
        </listitem>

        <listitem>
          <para>the <classname>@Min</classname> constraint on
          <varname>seatCount</varname> 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")</para>
        </listitem>

        <listitem>
          <para>the message for the <classname>@DecimalMax</classname>
          constraint on <varname>topSpeed</varname> shows how to format the
          validated value using the <varname>formatter</varname> object</para>
        </listitem>

        <listitem>
          <para>finally, the <classname>@DecimalMax</classname> constraint on
          <varname>price</varname> shows that parameter interpolation has
          precedence over expression evaluation, causing the
          <literal>$</literal> sign to show up in front of the maximum
          price</para>
        </listitem>
      </itemizedlist>

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

      <example id="example-expected-error-messages">
        <title>Expected error messages</title>

        <programlisting language="JAVA" role="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 );</programlisting>
      </example>
    </section>
  </section>

  <section id="section-custom-message-interpolation">
    <title>Custom message interpolation</title>

    <para>If the default message interpolation algorithm does not fit your
    requirements it is also possible to plug in a custom
    <classname>MessageInterpolator</classname> implementation.</para>

    <para>Custom interpolators must implement the interface
    <classname>javax.validation.MessageInterpolator</classname>. 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
    <methodname>Configuration#getDefaultMessageInterpolator()</methodname>.</para>

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

    <section id="section-resource-bundle-locator">
      <title><classname>ResourceBundleLocator</classname></title>

      <para>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
      <filename>ValidationMessages</filename>. In this situation Hibernate
      Validator's <classname>ResourceBundleLocator</classname> SPI can
      help.</para>

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

      <example id="example-using-specific-resource-bundle-locator">
        <title>Using a specific resource bundle</title>

        <programlisting language="JAVA" role="JAVA">Validator validator = Validation.byDefaultProvider()
		.configure()
		.messageInterpolator(
				new ResourceBundleMessageInterpolator(
						new PlatformResourceBundleLocator( "MyMessages" )
				)
		)
		.buildValidatorFactory()
		.getValidator();</programlisting>
      </example>

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

      <para>Besides <classname>PlatformResourceBundleLocator</classname>,
      Hibernate Validator provides another resource bundle locator
      implementation out of the box, namely
      <classname>AggregateResourceBundleLocator</classname>, 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. <xref
      linkend="example-using-aggregate-resource-bundle-locator"/> shows how to
      use <classname>AggregateResourceBundleLocator</classname>.</para>

      <example id="example-using-aggregate-resource-bundle-locator">
        <title>Using
        <classname>AggregateResourceBundleLocator</classname></title>

        <programlisting language="JAVA" role="JAVA">Validator validator = Validation.byDefaultProvider()
		.configure()
		.messageInterpolator(
				new ResourceBundleMessageInterpolator(
						new AggregateResourceBundleLocator(
								Arrays.asList(
										"MyMessages",
										"MyOtherMessages"
								)
						)
				)
		)
		.buildValidatorFactory()
		.getValidator();</programlisting>
      </example>

      <para>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.</para>
    </section>
  </section>
</chapter>
