<?xml version="1.0" encoding="UTF-8"?>
<!--
	~ JBoss, Home of Professional Open Source
	~ Copyright 2009, 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="validator-customconstraints">
  <title>Creating custom constraints</title>

  <para>The Bean Validation API defines a whole set of standard constraint
  annotations such as <classname>@NotNull</classname>,
  <classname>@Size</classname> etc. In cases where these buit-in constraints
  are not sufficient, you cean easily create custom constraints tailored to
  your specific validation requirements.</para>

  <section id="validator-customconstraints-simple">
    <title>Creating a simple constraint</title>

    <para>To create a custom constraint, the following three steps are
    required:</para>

    <itemizedlist>
      <listitem>
        <para>Create a constraint annotation</para>
      </listitem>

      <listitem>
        <para>Implement a validator</para>
      </listitem>

      <listitem>
        <para>Define a default error message</para>
      </listitem>
    </itemizedlist>

    <section id="validator-customconstraints-constraintannotation">
      <title>The constraint annotation</title>

      <para>This section shows how to write a constraint annotation which can
      be used to ensure that a given string is either completely upper case or
      lower case. Later on this constraint will be applied to the
      <property>licensePlate</property> field of the
      <classname>Car</classname> class from <xref
      linkend="validator-gettingstarted"/> to ensure, that the field is always
      an upper-case string.</para>

      <para>The first thing needed is a way to express the two case modes.
      While you could use <classname>String</classname> constants, a better
      approach is using a Java 5 enum for that purpose:</para>

      <example>
        <title>Enum <classname>CaseMode</classname> to express upper vs. lower
        case</title>

        <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06;

public enum CaseMode {
	UPPER,
	LOWER;
}</programlisting>
      </example>

      <para>The next step is to define the actual constraint annotation. If
      you've never designed an annotation before, this may look a bit scary,
      but actually it's not that hard:</para>

      <example id="example-defining-custom-constraint">
        <title>Defining the <classname>@CheckCase</classname> constraint
        annotation</title>

        <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06;

@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
public @interface CheckCase {

	String message() default "{org.hibernate.validator.referenceguide.chapter06.CheckCase." +
			"message}";

	Class&lt;?&gt;[] groups() default { };

	Class&lt;? extends Payload&gt;[] payload() default { };

	CaseMode value();

	@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
	@Retention(RUNTIME)
	@Documented
	@interface List {
		CheckCase[] value();
	}
}</programlisting>
      </example>

      <para>An annotation type is defined using the <code>@interface</code>
      keyword. All attributes of an annotation type are declared in a
      method-like manner. The specification of the Bean Validation API
      demands, that any constraint annotation defines</para>

      <itemizedlist>
        <listitem>
          <para>an attribute <property>message</property> that returns the
          default key for creating error messages in case the constraint is
          violated</para>
        </listitem>

        <listitem>
          <para>an attribute <property>groups</property> that allows the
          specification of validation groups, to which this constraint belongs
          (see <xref linkend="chapter-groups"/>). This must default to an
          empty array of type <classname>Class&lt;?&gt;</classname>.</para>
        </listitem>

        <listitem>
          <para>an attribute <classname>payload</classname> that can be used
          by clients of the Bean Validation API to assign custom payload
          objects to a constraint. This attribute is not used by the API
          itself. An example for a custom payload could be the definition of a
          severity:</para>

          <programlisting language="JAVA" role="JAVA">public class Severity {
	public interface Info extends Payload {
	}

	public interface Error extends Payload {
	}
}

public class ContactDetails {
	@NotNull(message = "Name is mandatory", payload = Severity.Error.class)
	private String name;

	@NotNull(message = "Phone number not specified, but not mandatory",
			payload = Severity.Info.class)
	private String phoneNumber;

	// ...
}</programlisting>

          <para>Now a client can after the validation of a
          <classname>ContactDetails</classname> instance access the severity
          of a constraint using
          <methodname>ConstraintViolation.getConstraintDescriptor().getPayload()</methodname>
          and adjust its behaviour depending on the severity.</para>
        </listitem>
      </itemizedlist>

      <para>Besides these three mandatory attributes there is another one,
      <property>value</property>, allowing for the required case mode to be
      specified. The name <property>value</property> is a special one, which
      can be omitted when using the annotation, if it is the only attribute
      specified, as e.g. in <code>@CheckCase(CaseMode.UPPER)</code>.</para>

      <para>In addition, the constraint annotation is decorated with a couple
      of meta annotations:</para>

      <itemizedlist>
        <listitem>
          <para><code>@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE
          })</code>: Defines the supported target element types for the
          constraint. <classname>@CheckCase</classname> may be used on fields
          (element type <classname>FIELD</classname>), JavaBeans properties as
          well as method return values (<classname>METHOD</classname>) and
          method/constructor parameters (<classname>PARAMETER</classname>).
          The element type <classname>ANNOTATION_TYPE</classname> allows for
          the creation of composed constraints (see <xref
          linkend="section-constraint-composition"/>) based on
          <classname>@CheckCase</classname>.</para>

          <para>When creating a class-level constraint (see <xref
          linkend="validator-usingvalidator-classlevel"/>), the element type
          <classname>TYPE</classname> would have to be used. Constraints
          targetting the return value of a constructor need to support the
          element type <classname>CONSTRUCTOR</classname>. Cross-parameter
          constraints (see <xref
          linkend="section-cross-parameter-constraints"/>) which are used to
          validate all the parameters of a method or constructor together,
          must support <classname>METHOD</classname> or
          <classname>CONSTRUCTOR</classname>, respectively.</para>
        </listitem>

        <listitem>
          <para><code>@Retention(RUNTIME)</code>: Specifies, that annotations
          of this type will be available at runtime by the means of
          reflection</para>
        </listitem>

        <listitem>
          <para><code>@Constraint(validatedBy =
          CheckCaseValidator.class)</code>: Marks the annotation type as
          constraint annotation and specifies the validator to be used to
          validate elements annotated with <classname>@CheckCase</classname>.
          If a constraint may be used on several data types, several
          validators may be specified, one for each data type.</para>
        </listitem>

        <listitem>
          <para><code>@Documented</code>: Says, that the use of
          <classname>@CheckCase</classname> will be contained in the JavaDoc
          of elements annotated with it</para>
        </listitem>
      </itemizedlist>

      <para>Finally, there is an inner annotation type named
      <classname>List</classname>. This annotation allows to specify several
      <classname>@CheckCase</classname> annotations on the same element, e.g.
      with different validation groups and messages. While also another name
      could be used, the Bean Validation specification recommends to use the
      name <classname>List</classname> and make the annotation an inner
      annotation of the corresponding constraint type.</para>
    </section>

    <section id="validator-customconstraints-validator">
      <title id="section-constraint-validator">The constraint
      validator</title>

      <para>Having defined the annotation, you need to create a constraint
      validator, which is able to validate elements with a
      <classname>@CheckCase</classname> annotation. To do so, implement the
      interface <classname>ConstraintValidator</classname> as shown
      below:</para>

      <example id="example-constraint-validator">
        <title>Implementing a constraint validator for the constraint
        <classname>@CheckCase</classname></title>

        <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06;

public class CheckCaseValidator implements ConstraintValidator&lt;CheckCase, String&gt; {

	private CaseMode caseMode;

	@Override
	public void initialize(CheckCase constraintAnnotation) {
		this.caseMode = constraintAnnotation.value();
	}

	@Override
	public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
		if ( object == null ) {
			return true;
		}

		if ( caseMode == CaseMode.UPPER ) {
			return object.equals( object.toUpperCase() );
		}
		else {
			return object.equals( object.toLowerCase() );
		}
	}
}</programlisting>
      </example>

      <para>The <classname>ConstraintValidator</classname> interface defines
      two type parameters which are set in the implementation. The first one
      specifies the annotation type to be validated
      (<classname>CheckCase</classname>), the second one the type of elements,
      which the validator can handle (<classname>String</classname>). In case
      a constraint supports several data types, a
      <classname>ConstraintValidator</classname> for each allowed type has to
      be implemented and registered at the constraint annotation as shown
      above.</para>

      <para>The implementation of the validator is straightforward. The
      <methodname>initialize()</methodname> method gives you access to the
      attribute values of the validated constraint and allows you to store
      them in a field of the validator as shown in the example.</para>

      <para>The <methodname>isValid()</methodname> method contains the actual
      validation logic. For <classname>@CheckCase</classname> this is the
      check whether a given string is either completely lower case or upper
      case, depending on the case mode retrieved in
      <classname>initialize()</classname>. Note that the Bean Validation
      specification recommends to consider <code>null</code> values as being
      valid. If <code>null</code> is not a valid value for an element, it
      should be annotated with <code>@NotNull</code> explicitly.</para>

      <section>
        <title>The <classname>ConstraintValidatorContext</classname></title>

        <para><xref linkend="example-constraint-validator"/> relies on the
        default error message generation by just returning
        <constant>true</constant> or <constant>false</constant> from the
        <methodname>isValid()</methodname> method. Using the passed
        <classname>ConstraintValidatorContext</classname> object it is
        possible to either add additional error messages or completely disable
        the default error message generation and solely define custom error
        messages. The <classname>ConstraintValidatorContext</classname> API is
        modeled as fluent interface and is best demonstrated with an
        example:</para>

        <example id="example-constraint-validator-context">
          <title>Using <classname>ConstraintValidatorContext</classname> to
          define custom error messages</title>

          <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.constraintvalidatorcontext;

public class CheckCaseValidator implements ConstraintValidator&lt;CheckCase, String&gt; {

	private CaseMode caseMode;

	@Override
	public void initialize(CheckCase constraintAnnotation) {
		this.caseMode = constraintAnnotation.value();
	}

	@Override
	public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
		if ( object == null ) {
			return true;
		}

		boolean isValid;
		if ( caseMode == CaseMode.UPPER ) {
			isValid = object.equals( object.toUpperCase() );
		}
		else {
			isValid = object.equals( object.toLowerCase() );
		}

		if ( !isValid ) {
			constraintContext.disableDefaultConstraintViolation();
			constraintContext.buildConstraintViolationWithTemplate(
					"{org.hibernate.validator.referenceguide.chapter03." +
					"constraintvalidatorcontext.CheckCase.message}"
			)
			.addConstraintViolation();
		}

		return isValid;
	}
}</programlisting>
        </example>

        <para><xref linkend="example-constraint-validator-context" os=""/>
        shows how you can disable the default error message generation and add
        a custom error message using a specified message template. In this
        example the use of the
        <classname>ConstraintValidatorContext</classname> results in the same
        error message as the default error message generation.</para>

        <tip>
          <para>It is important to add each configured constraint violation by
          calling <methodname>addConstraintViolation()</methodname>. Only
          after that the new constraint violation will be created.</para>
        </tip>

        <para>Refer to <xref linkend="section-custom-property-paths"/> to
        learn how to use <classname>the ConstraintValidatorContext</classname>
        API to control the property path of constraint violations for
        class-level constraints.</para>
      </section>
    </section>

    <section id="validator-customconstraints-errormessage">
      <title>The error message</title>

      <para>The last missing building block is an error message which should
      be used in case a <classname>@CheckCase</classname> constraint is
      violated. To define this, create a file<filename>
      ValidationMessages.properties</filename> with the following contents
      (see also <xref linkend="section-message-interpolation"/>):</para>

      <example>
        <title>Defining a custom error message for the
        <classname>CheckCase</classname> constraint</title>

        <programlisting>org.hibernate.validator.referenceguide.chapter06.CheckCase.message=Case mode must be {value}.</programlisting>
      </example>

      <para>If a validation error occurs, the validation runtime will use the
      default value, that you specified for the message attribute of the
      <classname>@CheckCase</classname> annotation to look up the error
      message in this resource bundle.</para>
    </section>

    <section id="validator-customconstraints-using">
      <title>Using the constraint</title>

      <para>You can now use the constraint in the <classname>Car</classname>
      class from the <xref linkend="validator-gettingstarted"/> chapter to
      specify that the <property>licensePlate</property> field should only
      contain upper-case strings:</para>

      <example id="example-car-with-checkcase">
        <title>Applying the <classname>@CheckCase</classname>
        constraint</title>

        <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06;

public class Car {

	@NotNull
	private String manufacturer;

	@NotNull
	@Size(min = 2, max = 14)
	@CheckCase(CaseMode.UPPER)
	private String licensePlate;

	@Min(2)
	private int seatCount;

	public Car ( String manufacturer, String licencePlate, int seatCount ) {
		this.manufacturer = manufacturer;
		this.licensePlate = licencePlate;
		this.seatCount = seatCount;
	}

	//getters and setters ...
}</programlisting>
      </example>

      <para>Finally, <xref linkend="example-using-checkcase"/> demonstrates
      how validating a <classname>Car</classname> object with an invalid
      license plate causes the <classname>@CheckCase</classname> constraint to
      be violated.</para>

      <example id="example-using-checkcase">
        <title>Validating objects with the @<classname>CheckCase</classname>
        constraint</title>

        <programlisting language="JAVA" role="JAVA">//invalid license plate
Car car = new Car( "Morris", "dd-ab-123", 4 );
Set&lt;ConstraintViolation&lt;Car&gt;&gt; constraintViolations =
		validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals(
		"Case mode must be UPPER.",
		constraintViolations.iterator().next().getMessage()
);

//valid license plate
car = new Car( "Morris", "DD-AB-123", 4 );

constraintViolations = validator.validate( car );

assertEquals( 0, constraintViolations.size() );</programlisting>
      </example>
    </section>
  </section>

  <section id="section-class-level-constraints">
    <title>Class-level constraints</title>

    <para>As discussed earlier, constraints can also be applied on the class
    level to validate the state of an entire object. Class-level constraints
    are defined in the same was as are property constraints. <xref
    linkend="example-implementing-classlevel-constraint"/> shows constraint
    annotation and validator of the
    <classname>@ValidPassengerCount</classname> constraint you already saw in
    use in <xref linkend="example-class-level"/>.</para>

    <example id="example-implementing-classlevel-constraint">
      <title>Implementing a class-level constraint</title>

      <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.classlevel;

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { ValidPassengerCountValidator.class })
@Documented
public @interface ValidPassengerCount {

	String message() default "{org.hibernate.validator.referenceguide.chapter06.classlevel." +
			"ValidPassengerCount.message}";

	Class&lt;?&gt;[] groups() default { };

	Class&lt;? extends Payload&gt;[] payload() default { };
}</programlisting>

      <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.classlevel;

public class ValidPassengerCountValidator
		implements ConstraintValidator&lt;ValidPassengerCount, Car&gt; {

	@Override
	public void initialize(ValidPassengerCount constraintAnnotation) {
	}

	@Override
	public boolean isValid(Car car, ConstraintValidatorContext context) {
		if ( car == null ) {
			return true;
		}

		return car.getPassengers().size() &lt;= car.getSeatCount();
	}
}</programlisting>
    </example>

    <para>As the example demonstrates, you need to use the element type
    <classname>TYPE</classname> in the <classname>@Target</classname>
    annotation. This allows the constraint to be put on type definitions. The
    validator of the constraint in the example receives a
    <classname>Car</classname> in the <methodname>isValid()</methodname>
    method and can access the complete object state to decide whether the
    given instance is valid or not.</para>

    <section id="section-custom-property-paths">
      <title>Custom property paths</title>

      <para>By default the constraint violation for a class-level constraint
      is reported on the level of the annotated type, e.g.
      <classname>Car</classname>.</para>

      <para>In some cases it is preferable though that the violation's
      property path refers to one of the involved properties. For instance you
      might want to report the <classname>@ValidPassengerCount</classname>
      constraint against the <property>passengers</property> property instead
      of the <classname>Car</classname> bean.</para>

      <para><xref linkend="example-custom-error"/> shows how this can be done
      by using the constraint validator context passed to
      <methodname>isValid()</methodname> to build a custom constraint
      violation with a property node for the property
      <property>passengers</property>. Note that you also could add several
      property nodes, pointing to a sub-entity of the validated bean.</para>

      <example id="example-custom-error">
        <title>Adding a new <classname>ConstraintViolation</classname> with
        custom property path</title>

        <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.custompath;

public class ValidPassengerCountValidator
		implements ConstraintValidator&lt;ValidPassengerCount, Car&gt; {

	@Override
	public void initialize(ValidPassengerCount constraintAnnotation) {
	}

	@Override
	public boolean isValid(Car car, ConstraintValidatorContext constraintValidatorContext) {
		if ( car == null ) {
			return true;
		}

		boolean isValid = car.getPassengers().size() &lt;= car.getSeatCount();

		if ( !isValid ) {
			constraintValidatorContext.disableDefaultConstraintViolation();
			constraintValidatorContext
					.buildConstraintViolationWithTemplate( "{my.custom.template}" )
					.addPropertyNode( "passengers" ).addConstraintViolation();
		}

		return isValid;
	}
}</programlisting>
      </example>
    </section>
  </section>

  <section id="section-cross-parameter-constraints">
    <title>Cross-parameter constraints</title>

    <para>Bean Validation distinguishes between two different kinds of
    constraints.</para>

    <para>Generic constraints (which have been discussed so far) apply to the
    annotated element, e.g. a type, field, method parameter or return value
    etc. Cross-parameter constraints, in contrast, apply to the array of
    parameters of a method or constructor and can be used to express
    validation logic which depends on several parameter values.</para>

    <para>In order to define a cross-parameter constraint, its validator class
    must be annotated with
    <classname>@SupportedValidationTarget(ValidationTarget.PARAMETERS)</classname>.
    The type parameter <varname>T</varname> from the
    <classname>ConstraintValidator</classname> interface must resolve to
    either <classname>Object</classname> or <classname>Object[]</classname> in
    order to receive the array of method/constructor arguments in the
    <methodname>isValid()</methodname> method.</para>

    <para>The following example shows the definition of a cross-parameter
    constraint which can be used to check that two <classname>Date</classname>
    parameters of a method are in the correct order:</para>

    <example>
      <title>Cross-parameter constraint</title>

      <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.crossparameter;

@Constraint(validatedBy = ConsistentDateParameterValidator.class)
@Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface ConsistentDateParameters {

	String message() default "{org.hibernate.validator.referenceguide.chapter06." +
			"crossparameter.ConsistentDateParameters.message}";

	Class&lt;?&gt;[] groups() default { };

	Class&lt;? extends Payload&gt;[] payload() default { };
}</programlisting>
    </example>

    <para>The definition of a cross-parameter constraint isn't any different
    from defining a generic constraint, i.e. it must specify the members
    <methodname>message()</methodname>, <methodname>groups()</methodname> and
    <methodname>payload()</methodname> and be annotated with
    <classname>@Constraint</classname>. This meta annotation also specifies
    the corresponding validator, which is shown in <xref
    linkend="example-cross-parameter-validator"/>. Note that besides the
    element types <classname>METHOD</classname> and
    <classname>CONSTRUCTOR</classname> also
    <classname>ANNOTATION_TYPE</classname> is specified as target of the
    annotation, in order to enable the creation of composed constraints based
    on <classname>@ConsistentDateParameters</classname> (see <xref
    linkend="validator-customconstraints-compound"/>).</para>

    <para><note>
        <para>Cross-parameter constraints are specified directly on the
        declaration of a method or constructor, which is also the case for
        return value constraints. In order to improve code readability, it is
        therefore recommended to chose constraint names - such as
        <classname>@ConsistentDateParameters</classname> - which make the
        constraint target apparent.</para>
      </note></para>

    <example id="example-cross-parameter-validator">
      <title>Generic and cross-parameter constraint</title>

      <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.crossparameter;

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ConsistentDateParameterValidator implements
		ConstraintValidator&lt;ConsistentDateParameters, Object[]&gt; {

	@Override
	public void initialize(ConsistentDateParameters constraintAnnotation) {
	}

	@Override
	public boolean isValid(Object[] value, ConstraintValidatorContext context) {
		if ( value.length != 2 ) {
			throw new IllegalArgumentException( "Illegal method signature" );
		}

		//leave null-checking to @NotNull on individual parameters
		if ( value[0] == null || value[1] == null ) {
			return true;
		}

		if ( !( value[0] instanceof Date ) || !( value[1] instanceof Date ) ) {
			throw new IllegalArgumentException(
					"Illegal method signature, expected two " +
							"parameters of type Date."
			);
		}

		return ( (Date) value[0] ).before( (Date) value[1] );
	}
}</programlisting>
    </example>

    <para>As discussed above, the validation target
    <varname>PARAMETERS</varname> must be configured for a cross-parameter
    validator by using the <classname>@SupportedValidationTarget</classname>
    annotation. Since a cross-parameter constraint could be applied to any
    method or constructor, it is considered a best practice to check for the
    expected number and types of parameters in the validator
    implementation.</para>

    <para>As with generic constraints, <literal>null</literal> parameters
    should be considered valid and <classname>@NotNull</classname> on the
    individual parameters should be used to make sure that parameters are not
    <literal>null</literal>.</para>

    <tip>
      <para>Similar to class-level constraints, you can create custom
      constraint violations on single parameters instead of all parameters
      when validating a cross-parameter constraint. Just obtain a node builder
      from the <classname>ConstraintValidatorContext</classname> passed to
      <methodname>isValid()</methodname> and add a parameter node by calling
      <methodname>addParameterNode()</methodname>. In the example you could
      use this to create a constraint violation on the end date parameter of
      the validated method.</para>
    </tip>

    <para>In rare situations a constraint is both, generic and
    cross-parameter. This is the case if a constraint has a validator class
    which is annotated with
    <code>@SupportedValidationTarget({ValidationTarget.PARAMETERS,
    ValidationTarget.ANNOTATED_ELEMENT})</code> or if it has a generic and a
    cross-parameter validator class.</para>

    <para>When declaring such a constraint on a method which has parameters
    and also a return value, the intended constraint target can't be
    determined. Constraints which are generic and cross-parameter at the same
    time, must therefore define a member
    <methodname>validationAppliesTo()</methodname> which allows the constraint
    user to specify the constraint's target as shown in <xref
    linkend="example-dual-cross-parameter-constraint"/>.</para>

    <example id="example-dual-cross-parameter-constraint">
      <title>Generic and cross-parameter constraint</title>

      <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.crossparameter;

@Constraint(validatedBy = {
		ScriptAssertObjectValidator.class,
		ScriptAssertParametersValidator.class
})
@Target({ TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface ScriptAssert {

	String message() default "{org.hibernate.validator.referenceguide.chapter06." +
			"crossparameter.ScriptAssert.message}";

	Class&lt;?&gt;[] groups() default { };

	Class&lt;? extends Payload&gt;[] payload() default { };

	String script();

	ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;
}</programlisting>
    </example>

    <para>The <classname>@ScriptAssert</classname> constraint has two
    validators (not shown), a generic and a cross-parameter one and thus
    defines the member <methodname>validationAppliesTo()</methodname>. The
    default value <varname>IMPLICIT</varname> allows to derive the target
    automatically in situations where this is possible (e.g. if the constraint
    is declared on a field or on a method which has parameters but no return
    value).</para>

    <para>If the target can not be determined implicitly, it must be set by
    the user to either <varname>PARAMETERS</varname> or
    <varname>RETURN_VALUE</varname> as shown in <xref
    linkend="example-dual-cross-parameter-constraint-usage"/>.</para>

    <example id="example-dual-cross-parameter-constraint-usage">
      <title>Specifying the target for a generic and cross-parameter
      constraint</title>

      <programlisting language="JAVA" role="JAVA">@ScriptAssert(script = "arg1.size() &lt;= arg0", validationAppliesTo = ConstraintTarget.PARAMETERS)
public Car buildCar(int seatCount, List&lt;Passenger&gt; passengers) {
	//...
}</programlisting>
    </example>
  </section>

  <section id="validator-customconstraints-compound">
    <title id="section-constraint-composition">Constraint composition</title>

    <para>Looking at the <property>licensePlate</property> field of the
    <classname>Car</classname> class in <xref
    linkend="example-car-with-checkcase"/>, you see three constraint
    annotations already. In complexer scenarios, where even more constraints
    could be applied to one element, this might become a bit confusing easily.
    Furthermore, if there was a <property>licensePlate</property> field in
    another class, you would have to copy all constraint declarations to the
    other class as well, violating the DRY principle.</para>

    <para>You can address this kind of problem by creating higher level
    constraints, composed from several basic constraints. <xref
    linkend="example-composed-constraint"/> shows a composed constraint
    annotation which comprises the constraints
    <classname>@NotNull</classname>, <classname>@Size</classname> and
    <classname>@CheckCase</classname>:</para>

    <example id="example-composed-constraint">
      <title>Creating a composing constraint
      <classname>@ValidLicensePlate</classname></title>

      <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.constraintcomposition;

@NotNull
@Size(min = 2, max = 14)
@CheckCase(CaseMode.UPPER)
@Target({ METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { })
@Documented
public @interface ValidLicensePlate {

	String message() default "{org.hibernate.validator.referenceguide.chapter06." +
			"constraintcomposition.ValidLicensePlate.message}";

	Class&lt;?&gt;[] groups() default { };

	Class&lt;? extends Payload&gt;[] payload() default { };
}</programlisting>
    </example>

    <para>To create a composed constraint, simply annotate the constraint
    declaration with its comprising constraints. If the composed constraint
    itself requires a validator, this validator is to be specified within the
    <classname>@Constraint</classname> annotation. For composed constraints
    which don't need an additional validator such as
    <classname>@ValidLicensePlate</classname>, just set
    <methodname>validatedBy()</methodname> to an empty array.</para>

    <para>Using the new composed constraint at the
    <property>licensePlate</property> field is fully equivalent to the
    previous version, where the three constraints were declared directly at
    the field itself:</para>

    <example>
      <title>Application of composing constraint
      <classname>ValidLicensePlate</classname></title>

      <programlisting language="JAVA" role="JAVA">package org.hibernate.validator.referenceguide.chapter06.constraintcomposition;

public class Car {

	@ValidLicensePlate
	private String licensePlate;

	//...
}</programlisting>
    </example>

    <para>The set of <classname>ConstraintViolations</classname> retrieved
    when validating a <classname>Car</classname> instance will contain an
    entry for each violated composing constraint of the
    <classname>@ValidLicensePlate</classname> constraint. If you rather prefer
    a single <classname>ConstraintViolation</classname> in case any of the
    composing constraints is violated, the
    <classname>@ReportAsSingleViolation</classname> meta constraint can be
    used as follows:</para>

    <example>
      <title>Using <classname>@ReportAsSingleViolation</classname></title>

      <programlisting language="JAVA" role="JAVA">//...
@ReportAsSingleViolation
public @interface ValidLicensePlate {

	String message() default "{org.hibernate.validator.referenceguide.chapter06." +
			"constraintcomposition.ValidLicensePlate.message}";

	Class&lt;?&gt;[] groups() default { };

	Class&lt;? extends Payload&gt;[] payload() default { };
}</programlisting>
    </example>
  </section>
</chapter>
