<?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="chapter-groups">
  <title>Grouping constraints</title>

  <para>All validation methods on <classname>Validator</classname> and
  <classname>ExecutableValidator</classname> discussed in earlier chapters
  also take a var-arg argument <parameter>groups</parameter>. So far we have
  been ignoring this parameter, but it is time to have a closer look.</para>

  <section>
    <title>Requesting groups</title>

    <para>Groups allow you to restrict the set of constraints applied during
    validation. One use case for validation groups are UI wizards where in
    each step only a specified subset of constraints should get validated. The
    groups targeted are passed as var-arg parameters to the appropriate
    validate method.</para>

    <para>Let's have a look at an example. The class
    <classname>Person</classname> in <xref linkend="example-person"/> has a
    <classname>@NotNull </classname>constraint on <property>name</property>.
    Since no group is specified for this annotation the default group
    <classname>javax.validation.groups.Default</classname> is assumed.</para>

    <note>
      <para>When more than one group is requested, the order in which the
      groups are evaluated is not deterministic. If no group is specified the
      default group <classname>javax.validation.groups.Default</classname> is
      assumed.</para>
    </note>

    <example id="example-person">
      <title>Person</title>

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

public class Person {

	@NotNull
	private String name;

	public Person(String name) {
		this.name = name;
	}

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

    <para>The class <classname>Driver</classname> in <xref
    linkend="example-driver"/> extends <classname>Person</classname> and adds
    the properties <property>age</property> and
    <property>hasDrivingLicense</property>. Drivers must be at least 18 years
    old (<classname>@Min(18)</classname>) and have a driving license
    (<classname>@AssertTrue</classname>). Both constraints defined on these
    properties belong to the group <classname>DriverChecks</classname> which
    is just a simple tagging interface.</para>

    <tip>
      <para>Using interfaces makes the usage of groups type-safe and allows
      for easy refactoring. It also means that groups can inherit from each
      other via class inheritance.</para>
    </tip>

    <example id="example-driver">
      <title>Driver</title>

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

public class Driver extends Person {

	@Min(
			value = 18,
			message = "You have to be 18 to drive a car",
			groups = DriverChecks.class
	)
	public int age;

	@AssertTrue(
			message = "You first have to pass the driving test",
			groups = DriverChecks.class
	)
	public boolean hasDrivingLicense;

	public Driver(String name) {
		super( name );
	}

	public void passedDrivingTest(boolean b) {
		hasDrivingLicense = b;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}</programlisting>

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

public interface DriverChecks {
}</programlisting>
    </example>

    <para>Finally the class Car (<xref linkend="example-car"/>) has some
    constraints which are part of the default group as well as
    <classname>@AssertTrue</classname> in the group
    <classname>CarChecks</classname> on the property
    <property>passedVehicleInspection</property> which indicates whether a car
    passed the road worthy tests.</para>

    <example id="example-car">
      <title>Car</title>

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

public class Car {
	@NotNull
	private String manufacturer;

	@NotNull
	@Size(min = 2, max = 14)
	private String licensePlate;

	@Min(2)
	private int seatCount;

	@AssertTrue(
			message = "The car has to pass the vehicle inspection first",
			groups = CarChecks.class
	)
	private boolean passedVehicleInspection;

	@Valid
	private Driver driver;

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

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

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

public interface CarChecks {
}</programlisting>
    </example>

    <para>Overall three different groups are used in the example:</para>

    <itemizedlist>
      <listitem>
        <para>The constraints on <property>Person.name</property>,
        <property>Car.manufacturer</property>,
        <property>Car.licensePlate</property> and
        <property>Car.seatCount</property> all belong to the
        <classname>Default</classname> group</para>
      </listitem>

      <listitem>
        <para>The constraints on <property>Driver.age</property> and
        <property>Driver.hasDrivingLicense</property> belong to
        <classname>DriverChecks</classname></para>
      </listitem>

      <listitem>
        <para>The constraint on
        <property>Car.passedVehicleInspection</property> belongs to the group
        <classname>CarChecks</classname></para>
      </listitem>
    </itemizedlist>

    <para><xref linkend="example-drive-away"/> shows how passing different
    group combinations to the <methodname>Validator#validate()</methodname>
    method results in different validation results.</para>

    <example id="example-drive-away">
      <title>Using validation groups</title>

      <programlisting language="JAVA" role="JAVA">// create a car and check that everything is ok with it.
Car car = new Car( "Morris", "DD-AB-123", 2 );
Set&lt;ConstraintViolation&lt;Car&gt;&gt; constraintViolations = validator.validate( car );
assertEquals( 0, constraintViolations.size() );

// but has it passed the vehicle inspection?
constraintViolations = validator.validate( car, CarChecks.class );
assertEquals( 1, constraintViolations.size() );
assertEquals(
		"The car has to pass the vehicle inspection first",
		constraintViolations.iterator().next().getMessage()
);

// let's go to the vehicle inspection
car.setPassedVehicleInspection( true );
assertEquals( 0, validator.validate( car ).size() );

// now let's add a driver. He is 18, but has not passed the driving test yet
Driver john = new Driver( "John Doe" );
john.setAge( 18 );
car.setDriver( john );
constraintViolations = validator.validate( car, DriverChecks.class );
assertEquals( 1, constraintViolations.size() );
assertEquals(
		"You first have to pass the driving test",
		constraintViolations.iterator().next().getMessage()
);

// ok, John passes the test
john.passedDrivingTest( true );
assertEquals( 0, validator.validate( car, DriverChecks.class ).size() );

// just checking that everything is in order now
assertEquals(
		0, validator.validate(
		car,
		Default.class,
		CarChecks.class,
		DriverChecks.class
).size()
);</programlisting>
    </example>

    <para>The first <methodname>validate()</methodname> call in <xref
    linkend="example-drive-away"/> is done using no explicit group. There are
    no validation errors, even though the property
    <property>passedVehicleInspection</property> is per default
    <constant>false</constant>. However, the constraint defined on this
    property does not belong to the default group.</para>

    <para>The next validation using the <classname>CarChecks</classname> group
    fails until the car passes the vehicle inspection. Adding a driver to the
    car and validating against <classname>DriverChecks</classname> again
    yields one constraint violation due to the fact that the driver has not
    yet passed the driving test. Only after setting
    <property>passedDrivingTest</property> to <literal>true</literal> the
    validation against <classname>DriverChecks</classname> passes.</para>

    <para>The last validate() call finally shows that all constraints are
    passing by validating against all defined groups.</para>
  </section>

  <section id="section-defining-group-sequences">
    <title>Defining group sequences</title>

    <para>By default, constraints are evaluated in no particular order,
    regardless of which groups they belong to. In some situations, however, it
    is useful to control the order constraints are evaluated.</para>

    <para>In the example from <xref linkend="example-drive-away"/> it could
    for instance be required that first all default car constraints are
    passing before checking the road worthiness of the car. Finally, before
    driving away, the actual driver constraints should be checked.</para>

    <para>In order to implement such a validation order you just need to
    define an interface and annotate it with
    <classname>@GroupSequence</classname>, defining the order in which the
    groups have to be validated (see <xref
    linkend="example-defining-group-sequence"/>). If at least one constraint
    fails in a sequenced group none of the constraints of the following groups
    in the sequence get validated.</para>

    <example id="example-defining-group-sequence">
      <title>Defining a group sequence</title>

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

@GroupSequence({ Default.class, CarChecks.class, DriverChecks.class })
public interface OrderedChecks {
}</programlisting>
    </example>

    <para><warning>
        <para>Groups defining a sequence and groups composing a sequence must
        not be involved in a cyclic dependency either directly or indirectly,
        either through cascaded sequence definition or group inheritance. If a
        group containing such a circularity is evaluated, a
        <classname>GroupDefinitionException</classname> is raised.</para>
      </warning>You then can use the new sequence as shown in in <xref
    linkend="example-group-sequence"/>.</para>

    <example id="example-group-sequence">
      <title>Using a group sequence</title>

      <programlisting language="JAVA" role="JAVA">Car car = new Car( "Morris", "DD-AB-123", 2 );
car.setPassedVehicleInspection( true );

Driver john = new Driver( "John Doe" );
john.setAge( 18 );
john.passedDrivingTest( true );
car.setDriver( john );

assertEquals( 0, validator.validate( car, OrderedChecks.class ).size() );</programlisting>
    </example>
  </section>

  <section id="section-default-group-class">
    <title>Redefining the default group sequence</title>

    <section>
      <title><classname>@GroupSequence</classname></title>

      <para>Besides defining group sequences, the
      <classname>@GroupSequence</classname> annotation also allows to redefine
      the default group for a given class. To do so, just add the
      <classname>@GroupSequence</classname> annotation to the class and
      specify the sequence of groups which substitute
      <classname>Default</classname> for this class within the
      annotation.</para>

      <para><xref linkend="example-rental-car"/> introduces a new class
      <classname>RentalCar</classname> with a redefined default group.</para>

      <example id="example-rental-car">
        <title>Class <classname>RentalCar</classname> with redefined default
        group</title>

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

@GroupSequence({ RentalChecks.class, CarChecks.class, RentalCar.class })
public class RentalCar extends Car {
	@AssertFalse(message = "The car is currently rented out", groups = RentalChecks.class)
	private boolean rented;

	public RentalCar(String manufacturer, String licencePlate, int seatCount) {
		super( manufacturer, licencePlate, seatCount );
	}

	public boolean isRented() {
		return rented;
	}

	public void setRented(boolean rented) {
		this.rented = rented;
	}
}</programlisting>

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

public interface RentalChecks {
}</programlisting>
      </example>

      <para>With this definition you can evaluate the constraints belonging to
      <classname>RentalChecks</classname>, <classname>CarChecks</classname>
      and <classname>RentalCar</classname> by just requesting the
      <classname>Default</classname> group as seen in <xref
      linkend="example-testCarIsRented"/>.</para>

      <example id="example-testCarIsRented">
        <title>Validating an object with redefined default group</title>

        <programlisting language="JAVA" role="JAVA">RentalCar rentalCar = new RentalCar( "Morris", "DD-AB-123", 2 );
rentalCar.setPassedVehicleInspection( true );
rentalCar.setRented( true );

Set&lt;ConstraintViolation&lt;RentalCar&gt;&gt; constraintViolations = validator.validate( rentalCar );

assertEquals( 1, constraintViolations.size() );
assertEquals(
		"Wrong message",
		"The car is currently rented out",
		constraintViolations.iterator().next().getMessage()
);

rentalCar.setRented( false );
constraintViolations = validator.validate( rentalCar );

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

      <note>
        <para>Since there must no cyclic dependency in the group and group
        sequence definitions one cannot just add
        <classname>Default</classname> to the sequence redefining
        <classname>Default</classname> for a class. Instead the class itself
        has to be added!</para>
      </note>

      <para>The <classname>Default</classname> group sequence overriding is
      local to the class it is defined on and is not propagated to associated
      objects. For the example this means that adding
      <classname>DriverChecks</classname> to the default group sequence of
      <classname>RentalCar</classname> would not have any effects. Only the
      group <classname>Default</classname> will be propagated to the
      <property>driver</property> association.</para>

      <para>Note that you can control the propagated group(s) by declaring a
      group conversion rule (see <xref
      linkend="section-group-conversion"/>).</para>
    </section>

    <section>
      <title><classname>@GroupSequenceProvider</classname></title>

      <para>In addition to statically redefining default group sequences via
      <classname>@GroupSequence</classname>, Hibernate Validator also provides
      an SPI for the dynamic redefinition of default group sequences depending
      on the object state.</para>

      <para>For that purpose you need to implement the interface
      <classname>DefaultGroupSequenceProvider</classname> and register this
      implementation with the target class via the
      <classname>@GroupSequenceProvider</classname> annotation. In the rental
      car scenario you could for instance dynamically add the
      <classname>CarChecks</classname> as seen in <xref
      linkend="example-implementing-using-default-group-sequence-provider"/>.</para>

      <example id="example-implementing-using-default-group-sequence-provider">
        <title>Implementing and using a default group sequence
        provider</title>

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

public class RentalCarGroupSequenceProvider
		implements DefaultGroupSequenceProvider&lt;RentalCar&gt; {

	@Override
	public List&lt;Class&lt;?&gt;&gt; getValidationGroups(RentalCar car) {
		List&lt;Class&lt;?&gt;&gt; defaultGroupSequence = new ArrayList&lt;Class&lt;?&gt;&gt;();
		defaultGroupSequence.add( RentalCar.class );

		if ( car != null &amp;&amp; !car.isRented() ) {
			defaultGroupSequence.add( CarChecks.class );
		}

		return defaultGroupSequence;
	}
}</programlisting>

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

@GroupSequenceProvider(RentalCarGroupSequenceProvider.class)
public class RentalCar extends Car {

	@AssertFalse(message = "The car is currently rented out", groups = RentalChecks.class)
	private boolean rented;

	public RentalCar(String manufacturer, String licencePlate, int seatCount) {
		super( manufacturer, licencePlate, seatCount );
	}

	public boolean isRented() {
		return rented;
	}

	public void setRented(boolean rented) {
		this.rented = rented;
	}
}</programlisting>
      </example>
    </section>
  </section>

  <section id="section-group-conversion">
    <title>Group conversion</title>

    <para>What if you wanted to validate the car related checks together with
    the driver checks? Of course you could pass the required groups to the
    validate call explicitly, but what if you wanted to make these validations
    occur as part of the <classname>Default</classname> group validation? Here
    <classname>@ConvertGroup</classname> comes into play which allows you
    during cascaded validation to use a different group than the originally
    requested one.</para>

    <para>Let's have a look at <xref
    linkend="example-group-conversion-dedault-to-driver-checks"/>. Here
    <code>@GroupSequence({ CarChecks.class, Car.class })</code> is used to
    combine the car related constraints under the
    <classname>Default</classname> group (see <xref
    linkend="section-default-group-class"/>). There is also a
    <code>@ConvertGroup(from = Default.class, to = DriverChecks.class)</code>
    which ensures the <classname>Default</classname> group gets converted to
    the <classname>DriverChecks</classname> group during cascaded validation
    of the <property>driver</property> association.</para>

    <example id="example-group-conversion-dedault-to-driver-checks">
      <title><classname>@ConvertGroup</classname> usage</title>

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

public class Driver {

	@NotNull
	private String name;

	@Min(
			value = 18,
			message = "You have to be 18 to drive a car",
			groups = DriverChecks.class
	)
	public int age;

	@AssertTrue(
			message = "You first have to pass the driving test",
			groups = DriverChecks.class
	)
	public boolean hasDrivingLicense;

	public Driver(String name) {
		this.name = name;
	}

	public void passedDrivingTest(boolean b) {
		hasDrivingLicense = b;
	}

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

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

@GroupSequence({ CarChecks.class, Car.class })
public class Car {

	@NotNull
	private String manufacturer;

	@NotNull
	@Size(min = 2, max = 14)
	private String licensePlate;

	@Min(2)
	private int seatCount;

	@AssertTrue(
			message = "The car has to pass the vehicle inspection first",
			groups = CarChecks.class
	)
	private boolean passedVehicleInspection;

	@Valid
	@ConvertGroup(from = Default.class, to = DriverChecks.class)
	private Driver driver;

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

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

    <para>As a result the validation in <xref
    linkend="example-group-conversion-test"/> succeeds, even though the
    constraint on <property>hasDrivingLicense</property> belongs to the
    <classname>DriverChecks</classname> group and only the
    <classname>Default</classname> group is requested in the
    <methodname>validate()</methodname> call.</para>

    <example id="example-group-conversion-test">
      <title>Test case for @ConvertGroup</title>

      <programlisting language="JAVA" role="JAVA">// create a car and validate. The Driver is still null and does not get validated
Car car = new Car( "VW", "USD-123", 4 );
car.setPassedVehicleInspection( true );
Set&lt;ConstraintViolation&lt;Car&gt;&gt; constraintViolations = validator.validate( car );
assertEquals( 0, constraintViolations.size() );

// create a driver who has not passed the driving test
Driver john = new Driver( "John Doe" );
john.setAge( 18 );

// now let's add a driver to the car
car.setDriver( john );
constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals(
		"The driver constraint should also be validated as part of the default group",
		constraintViolations.iterator().next().getMessage(),
		"You first have to pass the driving test"
);</programlisting>
    </example>

    <para>You can define group conversions wherever
    <classname>@Valid</classname> can be used, namely associations as well as
    method and constructor parameters and return values. Multiple conversions
    can be specified using <classname>@ConvertGroup.List</classname>.</para>

    <para>However, the following restrictions apply:<itemizedlist>
        <listitem>
          <para><classname>@ConvertGroup</classname> must only be used in
          combination with <classname>@Valid</classname>. If used without, a
          <classname>ConstraintDeclarationException</classname> is
          thrown.</para>
        </listitem>

        <listitem>
          <para>It is not legal to have multiple conversion rules on the same
          element with the same <parameter>from</parameter> value. In this
          case, a <classname>ConstraintDeclarationException</classname> is
          raised.</para>
        </listitem>

        <listitem>
          <para>The <parameter>from</parameter> attribute must not refer to a
          group sequence. A
          <classname>ConstraintDeclarationException</classname> is raised in
          this situation.</para>
        </listitem>
      </itemizedlist><note>
        <para>Rules are not executed recursively. The first matching
        conversion rule is used and subsequent rules are ignored. For example
        if a set of <classname>@ConvertGroup</classname> declarations chains
        group A to B and B to C, the group A will be converted to B and not to
        C.</para>
      </note></para>
  </section>
</chapter>
