Update To Groovy Validator

I made a change to the Groovy Validator project.

I changed the names of the annotations. For example, “IntAnnotation” is now “ValidInt”. The same change was made for the other types.

Here is the new README file:

This project has a few annotations that validate fields in POGOs, sort of like Grails constraints.I will attempt to make some annotations for properties in Groovy.Here is a POGO:

package info.shelfunit.properties.sample

class Book {

    int pages
    String title
    int year
}

It’s clean, and has no getters and setters. But what I do not like is there is no validation for your data. What if you want your String to be between 10 and 20 characters? What if you want your int field to be more than 100? And what’s to stop some dingo from trying to create a book object with less than 0 pages?

So I made some annotations that can do some validation for you.

package info.shelfunit.properties.sample

import validation.ValidInt
import validation.ValidString

class Book {

    @ValidInt( minValue = 30, maxValue = 400, throwEx = false )
    def pages
    @ValidString( minLength = 5, maxLength = 20, regEx = /^.*?[Gg]roovy.*$/  )
    String title
    int year
}

For POGOs, if a numeric field is declared as “def”, it will become null if the argument does not meet the validation constraints. If it is declared as a primitive, it will be set to 0 if the argument does not meet the validation constraints.

This project can also validate fields in immutable objects. In addition to using the annotations for the fields, you annotate the class with ImmutableValidator:

package info.shelfunit.properties.sample.immutable

import validation.ImmutableValidator
import validation.ValidInt
import validation.ValidLong
import validation.ValidString

@ImmutableValidator
class ImmutableObject002 {
    @ValidString( minLength = 5, maxLength = 10 )
    String firstString
    @ValidInt( minValue = 10, maxValue = 100 )
    int firstInt
    @ValidLong( maxValue = 100L, divisorSet = [ 5L, 7L ] )
    long firstLong
}

To process the annotations, put your properties in a Map, and add a boolean called “validation” and set it to true (since I couldn’t overload the Map constructor, I added a boolean):

def validatingImObject = new ImmutableObject002( 
    [ firstString: "Hi Again", firstInt: 11, firstLong: 22L ], true )

Adding the “throwEx” will throw an exception if the arguments do not meet the validation constraints. It is optional and is set to false by default. If an exception is thrown, it will print out the value and the constraints.

You might get a message like this:

"Hey" is a String with a length outside the range of 5 and 10 or does not match the regular expression ".*"

You can also use it with immutable objects annotated with the ImmutableValidator annotation. This would be a second boolean after the Map with your properties, since the first boolean controls validation:

def thirdImObject = new ImmutableObject002( 
[ firstString: "Hi Once Again", firstInt: 1234567, firstLong: 222L ], 
true, true )

In that case, you get a message with a line for each field. So you might get a message like this:

Groovy validation exception: 
"eeeeeeeeeee" is a String with a length outside the range of 5 to 10 characters or does not match the regular expression ".*" 
1234567 is an integer outside the range 10 to 100 or it is not divisible by anything in the set [1] 
222 is a long outside the range 0 to 100 or it is not divisible by anything in the set [5, 7]

If “throwException” is true for an immutable object and an exception is thrown, then the object will not be created.

This library can also handle final fields in mutable objects.

import groovy.transform.ToString
import validation.ValidInt
import validation.FinalFieldValidator

@ToString( includeNames = true )
@FinalFieldValidator
class Car {
    @ValidInt( minValue = 10, throwEx = false )
    int miles
    @ValidInt( minValue = 1990 )
    final int year
}

As with immutable validation, you need to use a map in the constructor to validate a final field.

def car = new Car( [ miles: 50, year: 2007 ], true, true )

Right now it only handles String, double, float, int and long. For String, it checks the string is checked against a minimum (“minLength”) and maximum (“maxLength”) length, and against a regular expression (“regEx”). For integers and longs, the field is checked against minimum (“minValue”) and maximum (“maxValue”) values, and a set of divisors (“divisorSet”). For double and float, the field is checked against minimum (“minValue”) and maximum (“maxValue”) values. There are defaults for all of these.

To use this project: Run

gradle distZip

and use build/libs/groovy-validator.jar in your project.

You’re welcome.

Image from “Psalterium Caroli Calvi ou Psalterium ad usum monasterii Sancti Dionysii, dit Psautier de Charles le Chauve”, a 9th century manuscript housed at the Bibliothèque nationale de France. Source gallica.bnf.fr / BnF; image assumed allowed under Fair Use.

Groovy Validator: Ready For Public Consumption

I think the Groovy Validators are ready for public consumption, at least as ready as they’ll ever be.

The basic idea is to allow you to use the same sort of constraints you get with Grails domain objects and use them in Groovy. The main difference is this uses annotations, and Grails uses static blocks.

I have tested them with POGOs, using final and mutable fields, and with immutable objects. Everything seems to work as intended.

Here is the README for the project:

This project has a few annotations that validate fields in POGOs, sort of like Grails constraints.

I will attempt to make some annotations for properties in Groovy.

Here is a POGO:

package info.shelfunit.properties.sample

class Book {

    int pages
    String title
    int year
}

It’s clean, and has no getters and setters. But what I do not like is there is no validation for your data. What if you want your String to be between 10 and 20 characters? What if you want your int field to be more than 100? And what’s to stop some dingo from trying to create a book object with less than 0 pages?

So I made some annotations that can do some validation for you.

package info.shelfunit.properties.sample

import validation.IntAnnotation
import validation.StringAnnotation

class Book {

    @IntAnnotation( minValue = 30, maxValue = 400, throwEx = false )
    def pages
    @StringAnnotation( minLength = 5, maxLength = 20, regEx = /^.*?[Gg]roovy.*$/  )
    String title
    int year
}

For POGOs, if a numeric field is declared as “def”, it will become null if the argument does not meet the validation constraints. If it is declared as a primitive, it will be set to 0 if the argument does not meet the validation constraints.

This project can also validate fields in immutable objects. In addition to using the annotations for the fields, you annotate the class with ImmutableValidator:

package info.shelfunit.properties.sample.immutable

import validation.ImmutableValidator
import validation.IntAnnotation
import validation.LongAnnotation
import validation.StringAnnotation

@ImmutableValidator
class ImmutableObject002 {
    @StringAnnotation( minLength = 5, maxLength = 10 )
    String firstString
    @IntAnnotation( minValue = 10, maxValue = 100 )
    int firstInt
    @LongAnnotation( maxValue = 100L, divisorSet = [ 5L, 7L ] )
    long firstLong
}

To process the annotations, put your properties in a Map, and add a boolean called “validation” and set it to true (since I couldn’t overload the Map constructor, I added a boolean):

def validatingImObject = new ImmutableObject002( 
    [ firstString: "Hi Again", firstInt: 11, firstLong: 22L ], true )

Adding the “throwEx” will throw an exception if the arguments do not meet the validation constraints. It is optional and is set to false by default. If an exception is thrown, it will print out the value and the constraints.

You might get a message like this:

"Hey" is a String with a length outside the range of 5 and 10 or does not match the regular expression ".*"

You can also use it with immutable objects annotated with the ImmutableValidator annotation. This would be a second boolean after the Map with your properties, since the first boolean controls validation:

def thirdImObject = new ImmutableObject002( 
[ firstString: "Hi Once Again", firstInt: 1234567, firstLong: 222L ], 
true, true )

In that case, you get a message with a line for each field. So you might get a message like this:

Groovy validation exception: 
"eeeeeeeeeee" is a String with a length outside the range of 5 to 10 characters or does not match the regular expression ".*" 
1234567 is an integer outside the range 10 to 100 or it is not divisible by anything in the set [1] 
222 is a long outside the range 0 to 100 or it is not divisible by anything in the set [5, 7]

If “throwException” is true for an immutable object and an exception is thrown, then the object will not be created.

This library can also handle final fields in mutable objects.

import groovy.transform.ToString
import validation.IntAnnotation
import validation.FinalFieldValidator

@ToString( includeNames = true )
@FinalFieldValidator
class Car {
    @IntAnnotation( minValue = 10, throwEx = false )
    int miles
    @IntAnnotation( minValue = 1990 )
    final int year
}

As with immutable validation, you need to use a map in the constructor to validate a final field.

def car = new Car( [ miles: 50, year: 2007 ], true, true )

Right now it only handles String, double, float, int and long. For String, it checks the string is checked against a minimum (“minLength”) and maximum (“maxLength”) length, and against a regular expression (“regEx”). For integers and longs, the field is checked against minimum (“minValue”) and maximum (“maxValue”) values, and a set of divisors (“divisorSet”). For double and float, the field is checked against minimum (“minValue”) and maximum (“maxValue”) values. There are defaults for all of these.

To use this project: Run

gradle distZip

and use build/libs/groovy-validator.jar in your project.
You’re welcome.

Image from a 13th century French manuscript at the Burgerbibliothek of Berne  (Wikipedia page here), image from e-Codices, assumed allowed under Fair Use.

Setting Final Fields In Groovy Validators

There is another update in Groovy Validators. I think I figured out how to get them to work to validate/constrain final fields in mutable objects.

Here is an object:

import groovy.transform.ToString
import validation.IntAnnotation
import validation.FinalFieldValidator

@ToString( includeNames = true )
@FinalFieldValidator
class Car {

    @IntAnnotation( minValue = 10, throwEx = false )
    int miles
    @IntAnnotation( minValue = 1990 )
    final int year
}

You can validate the final field (and the other one as well) by instantiating the object with a map for the fields, and a boolean to trigger the FinalFieldValidator annotation:

def car = new Car( [ miles: 50, year: 2008 ], true )

I am still working on the Spock tests. I hope to have things wrapped up this weekend.

You’re welcome.

Hidden Annotation in Groovy Validators

There is another update to the Groovy Validators. (I know I keep saying I am done, but like The Godfather, I keep getting pulled back in.)

I made a new annotation @Hidden. Here is the summary from the groovydoc:

The purpose of this annotation is to help you keep your private fields private.

Suppose you have a field in an object you want to change. In that case, making it final will not work. But you also do not want code outside the object to change it. Making it private won’t work in Groovy either. You could write a setter that takes an argument and does nothing with it, over and over. Or you could use this annotation.

Here is an example on how to use it:

It is said you are only as old as you feel. Suppose going on a yoga retreat helps you feel a year younger, but visiting your in-laws makes you feel a year older. (One way to avoid this is to not marry a Scala programmer.)

class AgeHolder
    @Hidden
    int perceivedAge
    
    AgeHolder( argAge ) {
        this.perceivedAge = argAge
    }
    
    def visitYogaRetreat() {
        perceivedAge--
    }
    
    def visitInLaws() {
        perceivedAge++
    }
}

You’re welcome.

Image from Wikimedia, assumed allowed under Fair Use. Image from the Rabbula Gospels, a 6th century Syriac Gospel manuscript.

 

Taking a Break From Groovy Validators

I worked a bit on my Groovy Validator project.

I was trying to process the annotation in a class referenced by GroovyASTTransformationClass. I thought that there were a couple of ways it could be done, but none of them seemed to work. I was actually able to create the setter in the class, I inserted a static initializer that called my current annotation transformer that works at runtime, but neither seemed to work.

I plan on moving on to other things for the time being. I might ask for some help on the Groovy mailing list, but if I do, I will have to formulate a coherent request. I asked for help on the list a while back, and I think I included too much detail because I never got a response.

You’re welcome.

Image from Aurora Consurgens, a 15th century manuscript housed at Central Library of Zurich. Image from e-Codices. This image is assumed to be allowed under Fair Use.

Issue With Groovy Validator

I am having some issues with the Groovy Validator project. Here is an email I sent to the Groovy user mailing list:

I am making some annotations to do validation on POGOs. The github project is here:
https://github.com/emacadie/groovy-validator

One of my annotations is @StringAnnotation, which has the properties minLength, maxLength and regEx. I also have annotations for ints, longs, floats and doubles.

I got them working with POGOs and POGOs that have the @Immutable annotation.

So far I have the annotations working for POGOs by calling a static method on a class called AnnotationProcessor, and the annotations are handled by @Immutable objects with AstImmutableConstructorTransform.

https://github.com/emacadie/groovy-validator/blob/proper_annotations/src/main/groovy/info/shelfunit/properties/annotations/AnnotationProcessor.groovy
https://github.com/emacadie/groovy-validator/blob/proper_annotations/src/main/groovy/info/shelfunit/properties/annotations/AstImmutableConstructorTransform.groovy
I decided I would like to process the individual annotations with their own processor, so I started with the @StringAnnotation.
The AST class is here:
https://github.com/emacadie/groovy-validator/blob/proper_annotations/src/main/groovy/info/shelfunit/properties/annotations/StringAnnotationTransform.groovy
There are a lot of println statements since I am trying to figure all of this stuff out.

The issue that I am having is that when I run some tests the StringAnnotationTransform does not seem to work. All the strings wind up null in my tests.
Here is the command I use:
gradle clean; gradle -Dtest.single=BookSequelTest test -info

Yet, when I try these in the Groovy console (which I run with “gradle console”) things work as expected.

Why does everything work in the console but not in tests? Any ideas?

Image from the Orsha Gospel, a 13th-century Slavonic manuscript housed at the V.I. Vernadsky National Library of Ukraine (Wikipedia page here), image from World Document Library, image assumed allowed under Fair Use.

2015-01-25 Update

I have a bit more free time on my hands these days.

I plan on getting more into Clojure soon. I might use it to build my mail server. Or I might try that with GPars first.

Before that, I will take another crack at Groovy Validator. I would like to get rid of the static class that processes the annotations for mutable objects. I have one way of processing them for immutable objects, and another for mutable. It should be the same to the end developer.

 

Image from a Psalter from the Nonnberg Convent, an 13th century Gospel manuscript housed at Bavarian State Library, webpage information here, image from World Document Library, image assumed allowed under Fair Use.

Groovy Validator: Better Than Cowbell

There are more updates to the Groovy Validator project.

I got regular expressions working for StringAnnotation in POGOs and immutable objects.

IntAnnotation and LongAnnotation now have an optional argument called “divisorSet”. If you want your number to be even, you can add the set [2]. If you want it to be divisible by 3 or 7, you add [3, 7].

I also changed the exception messages and did some refactoring. There was some repetition I moved into methods. Handling doubles and floats was pretty much the same, and integers and longs were almost the same.

I think the README and groovydocs explain things pretty well.

You’re welcome.

Image from Liber Divinorum Operum (The Book of Divine Works), a 12th century manuscript by Hildegard von Bingen housed at the State Library of Lucca, image from World Document Library, image assumed allowed under Fair Use.

Regular Expressions in Groovy Validator

There is another update to the Groovy Validator project. I added regular expressions to the StringAnnotation class. I have not added it to the class that performs validation for immutable objects.

Before, all you could do is validate a String by length. Now you can also use regular expressions:

import info.shelfunit.properties.annotations.ImmutableValidator
import info.shelfunit.properties.annotations.StringAnnotation

@ImmutableValidator
class ImmutableRegEx {
   
    @StringAnnotation( minLength = 10, regEx = /^.*?[Gg]roovy.*$/ ) 
    String groovyString
    @StringAnnotation( regEx = /\d{4}?-\d\d-\d\d/  ) 
    String yearWithDay
    @StringAnnotation( minLength = 6, maxLength = 10, regEx = /^(?=.*[0-9].*[0-9])[0-9a-zA-Z]{8,12}$/ )
    String password
}

The default is the catch-all “.*”, so the annotation will still work with no regular expression provided by the user. This can be used with POGOs and processed by AnnotationProcessor.process, and it can be used with ImmutableValidator. If you are going to use regular expressions with ImmutableValidator, you must delimit the regex string with slashes. If you are using regular expressions with AnnotationProcessor, you can delimit the regex string with slashes or quotes. But, as always, if you use quotes, you have to do a lot of escaping yourself.

The regular expression has to be unbroken on one line. This means you cannot use comments to document them.

I also beefed up the annotation AST transformer for immutable objects. I put annotations on every field in the immutable objects I was testing, and it was only now that I realized that it choked on a field in an immutable object that did not have any validation annotation. I think I fixed that.

I keep thinking I am done, but I have one more change I think I can make.

You’re welcome.

Another Update For Groovy Validators

I made an enhancement to the Groovy Validator project.

I added a boolean called “throwException” that will throw an exception if one of the fields does not validate. It is optional, and defaults to false. If an exception is thrown, it will print out the value and the constraints.

You can use it for POGOs with the AnnotationProcessor class like this:

AnnotationProcessor.process( Book, true )

You might get a message like this:

"Hey" is a String with a length outside the range of 5 and 10"

You can also use it with immutable objects annotated with the AstImmutableConstructor annotation. This would be a second boolean after the Map with your properties, since the first boolean controls validation:

def thirdImObject = new ImmutableObject002( 
[ firstString: "Hi Once Again", firstInt: 123456789, firstLong: 22L ], 
true, true )

In that case, you get a message with a line for each field. So you might get a whopper message like this:

Groovy validation exception: 
"eeeeeeeeeee" is a String with a length outside the range of 5 to 10 characters 
"NNNNNNNNNNNNNNNN" is a String with a length outside the range of 0 to 15 characters 
101.0 is a double outside the range 10.0 and 100.0 
101.0 is a float outside the range 10.0 and 100.0
101 is an integer outside the range 10 and 100 
101 is a long outside the range 0 and 100

If “thowException” is true for a POGO, the field will either retains its pre-existing value (if it had one) or be set to null. If “throwException” is true for an immutable object, the object will not be created.

You’re welcome.