Package jvalidations.cookbook

Source Code of jvalidations.cookbook.Cookbook

package jvalidations.cookbook;

import static jvalidations.SyntaxSupport._else;
import static jvalidations.SyntaxSupport.Parameters.*;
import static jvalidations.SyntaxSupport.Conditions.condition;
import static jvalidations.SyntaxSupport.Cardinalities.atLeast;
import jvalidations.Validatable;
import jvalidations.ValidationSyntax;
import jvalidations.DefaultValidationBuilder;
import jvalidations.SyntaxSupport;
import org.hamcrest.core.*;
import static org.hamcrest.core.DescribedAs.describedAs;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.notNullValue;
import org.hamcrest.text.IsEmptyString;
import static org.hamcrest.text.IsEmptyString.isEmptyString;
import org.hamcrest.number.OrderingComparison;
import static org.hamcrest.number.OrderingComparison.greaterThan;

public class Cookbook {
    /**
     * This page explains how to do the most common validation tasks and should be read following the tutorial, or for reference.
     */
    public static class Section_ValidatingValuesReturnedFromMethods {
        /**
         * Most places where you use a field name you can also use a method name.  The method must take no arguments
         * and you indicate its a method simply by putting <b>()</b> at the end of it.
         */
        public static class Customer implements Validatable<Customer.ValidationReport> {

            public interface ValidationReport {
                void isNull(String fieldOrMethodName);
            }

            public String someQueryMethod() {
                return null;
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that("someQueryMethod()", notNullValue(), _else(report, "isNull", fieldName()));
            }
        }

        /**
         * This will call <domaincode>someQueryMethod()</domaincode> on the instance of <domaincode>Customer</domaincode> being
         * validated, then validate that the result of that method is not null.
         */
    }//ignore

    public static class Section_SpecifyingParametersForYourValidationFailureCallbackMethod {
        /**
         * There may be a time where you want to have parameters from the context of the validation rule passed
         * through to your validation failure callback method.  We have already seen this some for <method>fieldName()</method>.
         * But other possibilities exist like:
         * <br/>
         * <ul>
         * <li><method>requiredCount()</method> - the number specified in a cardinality.  For example, it would be 2 in <method>atLeast(2)</method></li>
         * <li><method>actualCount()</method> - the number of successful matches in a rule with cardinality. For example, if you stated <method>atLeast(2)</method> of something, but only 1 was successful, this number would be 1</li>
         * <li><method>fieldNames()</method> - the names of the fields in a rule with cardinality. For example, if you stated <method>atLeast(1).of("age","name")</method>  this would be a string array containing "age" and "name"</li>
         * <li><method>fieldName()</method> - the name of the field in a rule without cardinality. For example, if you stated <method>validates.that("age",...</method> this would be the string "age"</li>
         * <li><method>constant()</method> - any value that you specify as a constant will be passed through untouched</li>
         * <li><method>failureDescription()</method> - a string describing the validation failure.  This string is built into the particular Hamcrest matcher.  You can specify your own string using <method>DescribedAs.describedAs()</method> as shown below.</li>
         * </ul>
         * <br/>
         * Some examples:
         */
        public static class Customer implements Validatable<Customer.ValidationReport> {
            private String name;
            private int age;

            public interface ValidationReport {
                void describedFailure(String fieldName, String failureDescription);
                void failureWithErrorCode(String errorCode);
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that("name", notNullValue(), _else(report, "describedFailure", fieldName(), failureDescription()));
                validates.that("age",
                        describedAs("ErrorCode 1002",greaterThan(21)),
                        _else(report, "failureWithErrorCode", failureDescription()));
            }
        }
        /**
         * All of these parameter methods are available as static imports from <class>SyntaxSupport.Parameters</class>.
         */
    }//ignore

    public static class Section_ValidatingMultipleFields {
        /**
         * Its very common to want to say "at least 2 of these 3 fields should be non null":
         */
        public static class Customer implements Validatable<Customer.ValidationReport>{
            private String name;
            private String email;
            private String level;

            public interface ValidationReport {
                void isNull(String []fieldNames, int requiredCount, int ActualCount);
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that(
                        atLeast(2).of("name","email","level"),
                        notNullValue(),
                        _else(report, "isNull", fieldNames(), requiredCount(), actualCount()));
            }
        }

        /**
         * <method>atLeast()</method> is available as a static method on <class>SyntaxSupport.Cardinalities</class>, along with other cardinality methods like
         * <method>exactly()</method>, <method>all()</method> and <method>both</method>.
         * <br/>
         * <br/>
         * <method>fieldNames()</method> is available as a static method on <class>SyntaxSupport.Parameters</class>.  It will make the list of field names in the validation
         * available as a String array to your callback method.  <method>requiredCount()</method> and <method>actualCount()</method> are also on <class>SyntaxSupport.Parameters</class>
         * and make the number of fields required by the validation cardinality and the actual number of valid fields respectively available to you callback method.
         */
    }//ignore

    public static class Section_ValidatingAssociatedObjects {
        /**
         * Lets say our <domaincode>Customer</domaincode> has an <domaincode>Address</domaincode> object associated with it and when validating the <domaincode>Customer</domaincode>
         * we want to rope in validation of the <domaincode>Address</domaincode>.  Well lets walk through that.  The first step is to make the <domaincode>Address</domaincode>
         * validatable...
         */
        public static class Address implements Validatable<Address.ValidationReport>{
            private String addressLine1;
            private String addressLine2;
            private String city;
            private String postCode;

            public interface ValidationReport {
                void required(String fieldName);
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that("addressLine1", not(isEmptyString()), _else(report, "required", fieldName()));
                validates.that("postCode", not(isEmptyString()), _else(report, "required", fieldName()));
            }
        }

        /**
         * And now lets look at what we have to do with our <domaincode>Customer</domaincode>.  The main thing to look out for is that the validation report
         * in <domaincode>Customer</domaincode> must extend the validation report in <domaincode>Address</domaincode>.
         * Then all you do is <method>validates.associated()</method>
         */
        public static class Customer implements Validatable<Customer.ValidationReport>{
            private String name;
            private String email;
            private Address address;

            public interface ValidationReport extends Address.ValidationReport {
                void isBlank(String fieldName);
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that("name", not(isEmptyString()), _else(report, "isBlank", fieldName()));
                validates.that("email", not(isEmptyString()), _else(report, "isBlank", fieldName()));
                validates.associated("address", report);
            }
        }
        /**
         * The first two validation rules are for the fields on the <domaincode>Customer</domaincode>, and the third line says
         * "descend into the address instance and validate it too".  There may be futher <method>validates.associated()</method>
         * calls in <domaincode>Address</domaincode> if needed.  Just remember that the validation report of a domain class
         * needs to extend all the validation reports of its children.
          */
    }//ignore

    public static class Section_InheritValidationRulesFromSuperClass {
        /**
         * If you have got validation rules in a super class, you will also want to make sure they apply to sub classes.
         * If you want to do this there is a few bits of garbage you need to do with the generics:
         * <ul>
         * <li>Your super class needs to be genericized on the validation report, guaranteeing that it is at least a sub class
         * of its own validation report</li>
         * <li>Your sub class needs to bind its validation report to the abstract class and it does not need to implement
         * <interface>Validatable</interface>.</li>
         * </ul>
         * Goodness - I find this stuff so hard to remember.
         * <br/>
         * <br/>
         * Now you can call <method>super.buildValidation()</method> and ensure that the sub class validation report
         * extends the super class validation report
         */
        public static abstract class AbstractCustomer<R extends AbstractCustomer.ValidationReport> implements Validatable<R>{
            private String name;

            public interface ValidationReport {
                void isBlank(String fieldName);
            }

            public void buildValidation(ValidationSyntax validates,
                                        R report) {
                validates.that("name",not(isEmptyString()), _else(report, "isBlank", fieldName()));
            }
        }
        /**
         * And now the subclass...
         */
        public static class Customer extends AbstractCustomer<Customer.ValidationReport>{
            private String email;

            public interface ValidationReport extends AbstractCustomer.ValidationReport {
                void required(String fieldName);
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                super.buildValidation(validates, report);
                validates.that("name",not(isEmptyString()), _else(report, "isBlank", fieldName()));
            }
        }
        /**
         * Notice that <method>buildValidation()</method> in the super class uses <class>R</class> as its type.  And that the sub class
         * does not need to implement <interface>Validatable</interface> because it binds its version of the validation report to the
         * <method>buildValidation()</method> in the super class.  Phew!
         */
    }//ignore

    public static class Section_MakingValidationRulesConditional {
        /**
         * Sometimes you only want certain validation rules to fire in certain cases.  For example, only validate address line 2
         * if address line 1 has a value.  Or only validate the address of a customer if the customer is active.  You can do this kind
         * of thing by tacking an <method>on()</method> clause onto the end of your validation rule.
         */
        public static class Address implements Validatable<Address.ValidationReport>{
            private String addressLine1;
            private String addressLine2;

            public interface ValidationReport {
                void required(String fieldName);
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that("addressLine1", not(isEmptyString()), _else(report, "required", fieldName()));
                validates.that(
                        "addressLine2",
                        not(isEmptyString()),
                        _else(report, "required", fieldName())
                ).on(condition("addressLine1", not(isEmptyString())));
            }
        }
        /**
         * <method>condition()</method> is available as a static method on <class>SyntaxSupport.Conditions</class>.  In this case we are supplying it a field name
         * and a validation.  Only if this is true for the object being validated will the associated validation rule fire.  A few things to note:
         * <ul>
         * <li>If you omit the validation part, <method>TrueValidation.isTrue()</method> will be used instead.  So you can say <method>on(condition("isActive"))</method></li>
         * <li>Zero arg methods can be used in place of field names.  So you can say <method>on(condition("isActive()"))</method></li>
         * </ul>
         * <br/>
         * <br/>
         * You can use the logical operators for validation rules like and and or and not in these conditions - see the section below on logical operators.
         */
    }//ignore

    public static class Section_LogicalOperationsForValidationRules {
        /**
         * Sometimes you may want to combine validations into a logical expression to state your intent.
         * I am talking about the hamcrest <method>AllOf.allOf()</method>, <method>AnyOf.anyOf()()</method>, <method>IsNot.not()</method> and the like.
         * <br/>
         * <br/>
         * Say you want a string to either start with Jo or Ac....
         */
        public static class Customer implements Validatable<Customer.ValidationReport>{
            private String name;

            public interface ValidationReport {
                void somethingWrong();
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that(
                        "name",
                        AnyOf.anyOf(StringStartsWith.startsWith("Jo"),StringStartsWith.startsWith("Ac")),
                        _else(report, "somethingWrong")); //same as isBetween()
            }
        }
        /**
         * The same kind of thing goes for <method>AllOf.allOf()()</method> and <method>IsNot.not()</method>.
         */
    }//ignore

    public static class Section_SelectingWhichValidationRulesToFireAtValidationTime {
        /**
         * Sometimes you want to fire a particular arrangement of validation rules based on conditions that are
         * only known to the caller.  The <method>on()</method> clause lets you state conditions for validation rules
         * that depend on the objects state.  But what if you want to exclude a particular validation rule because, say,
         * the class doing the validation knows that a special offer is on today and a certain rule therefore does not apply.
         * <br/>
         * <br/>
         * Well you can do this by tagging your validation rules, then excluding particular tags at validation time. A tag can be any old object by the way.
         */
        public static class Customer implements Validatable<Customer.ValidationReport>{
            private int creditAvailable;

            public interface ValidationReport {
                void insufficientCredit();
            }

            public void buildValidation(ValidationSyntax validates,
                                        ValidationReport report) {
                validates.that(
                        "creditAvailable",
                        greaterThan(1000),
                        _else(report, "insufficientCredit")
                ).tags("Due Diligence");
            }
        }

        public void demonstrateRemovingTags() {
            Customer customer = new Customer();
            Customer.ValidationReport report = null; //its only a demo
            DefaultValidationBuilder builder = new DefaultValidationBuilder();
            customer.buildValidation(builder, report);
            builder.removeTags("Due Diligence");
            builder.validate(customer);
        }
        /**
         * And now, minus your due diligence,  you can have your very own credit crunch :-)
         */
    }//ignore

    public static class Section_ExternalizingTheDefinitionOfValidationRules {
        /**
         * This is something that came up recently only in discussion and has not been thought through, spiked
         * or implemented :-)  But its not uncommon for a need to arise to externalize validation rules for a business
         * object.  While one of the goals of JValidations is to encapsulate the validation rules for an object, it is
         * worthwhile considering if validation rules for an object could be externalised through the use of OGNL say.
         * It would be possible to use the JValidation syntax in that OGNL file, and have the domain object load its
         * definition from the OGNL file and that might solve the problem.  But this is an exercise for another day, and
         * its just noted here for interest.
         */

    }//ignore

    public static class Section_PuttingThingsTogetherToDefineYourOwnValidationCallbackNamingScheme {
    }//ignore
    /*END*/
TOP

Related Classes of jvalidations.cookbook.Cookbook

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.