Skip to main content

Generic PropertyDescriptor puzzler

Posted by evanx on September 8, 2006 at 1:34 AM PDT

Consider the implementation of a validator, to do a range check.

Since I have a sneaky suspicion we gonna need to communicate with the user
when the validation fails, lets prepare those messages in a resource-injectible bundle
of bytes as follows. The problem with IT is all these darn users! They keep getting
in the way of real progress, wanting silly features, finding silly bugs...

<font color=#000099><b>class</b></font> ValidatorResource {

    @ResourceAnnotation()<font color=#666666></font>
    String property = <font color=#99006b>"The property '%s'"</font>;

    @ResourceAnnotation()<font color=#666666></font>
    String isNull = <font color=#99006b>"is null"</font>;       
   
    @ResourceAnnotation()<font color=#666666></font>
    String lessThanMinimum = <font color=#99006b>"is less than %s"</font> ;
   
    @ResourceAnnotation()<font color=#666666></font>
    String equalsMinimum = <font color=#99006b>"is equal to, not greater than %s"</font>;
   
    @ResourceAnnotation()<font color=#666666></font>
    String greaterThanMaximum = <font color=#99006b>"is greater than %s"</font>;
   
    @ResourceAnnotation()<font color=#666666></font>
    String equalsMaximum = <font color=#99006b>"is equal to, not less than %s"</font>;

    <font color=#000099><b>public</b></font> ValidatorResource() {
        configurator.<b>configure</b>(<font color=#000099><b>this</b></font>);
    }
}

Now because we got so many types of numbers and dates and what-not, let's implement
a generic range validator as follows, where our value type is Comparable.

<font color=#000099><b>abstract</b></font> <font color=#000099><b>class</b></font> ComparableRangeValidator&lt;Value <font color=#000099><b>extends</b></font> Comparable&gt; {
    <font color=#000099><b>public</b></font> <font color=#000099><b>static</b></font> <font color=#000099><b>final</b></font> ValidatorResource resource = <font color=#000099><b>new</b></font> ValidatorResource();
   
    Value minimum;
    Value maximum;
    <font color=#000099><b>boolean</b></font> nullable;
    <font color=#000099><b>boolean</b></font> exclusive;
   
    <font color=#000099><b>public</b></font> ComparableRangeValidator() {
    }
   
    <font color=#000099><b>private</b></font> String <b>format</b>(NBeanProperty property, String format, Object ... args) {
        <font color=#000099><b>return</b></font> formatter.<b>format</b>(resource.property, property.<b>getPropertyLabel</b>()) +
                formatter.<b>format</b>(format, args);
    }
   
    <font color=#000099><b>public</b></font> String <b>validate</b>(NBeanProperty property, Value value) {
        <font color=#000099><b>if</b></font> (value == <font color=#000099><b>null</b></font>) {
            <font color=#000099><b>if</b></font> (!nullable) {
                <font color=#000099><b>return</b></font> <b>format</b>(property, resource.isNull);
            }
            <font color=#000099><b>return</b></font> <font color=#000099><b>null</b></font>;
        }
        <font color=#000099><b>if</b></font> (value.<b>compareTo</b>(minimum) &lt; 0) {
            <font color=#000099><b>return</b></font> <b>format</b>(property, resource.lessThanMinimum, minimum);
        }
        <font color=#000099><b>if</b></font> (value.<b>compareTo</b>(minimum) == 0 &amp;&amp; exclusive) {
            <font color=#000099><b>return</b></font> <b>format</b>(property, resource.equalsMinimum, minimum);
        }
        <font color=#000099><b>if</b></font> (value.<b>compareTo</b>(maximum) &gt; 0) {
            <font color=#000099><b>return</b></font> <b>format</b>(property, resource.greaterThanMaximum, maximum);
        }
        <font color=#000099><b>if</b></font> (value.<b>compareTo</b>(maximum) == 0 &amp;&amp; exclusive) {
            <font color=#000099><b>return</b></font> <b>format</b>(property, resource.equalsMaximum, maximum);
        }
        <font color=#000099><b>return</b></font> <font color=#000099><b>null</b></font>;
    }
   
    <font color=#000099><b>public</b></font> <font color=#000099><b>void</b></font> <b>setExclusive</b>(<font color=#000099><b>boolean</b></font> exclusive) {
        <font color=#000099><b>this</b></font>.exclusive = exclusive;
    }
   
    <font color=#000099><b>public</b></font> <font color=#000099><b>boolean</b></font> <b>isExclusive</b>() {
        <font color=#000099><b>return</b></font> exclusive;
    }
   
    <font color=#000099><b>public</b></font> <font color=#000099><b>void</b></font> <b>setNullable</b>(<font color=#000099><b>boolean</b></font> nullable) {
        <font color=#000099><b>this</b></font>.nullable = nullable;
    }
   
    <font color=#000099><b>public</b></font> <font color=#000099><b>boolean</b></font> <b>isNullable</b>() {
        <font color=#000099><b>return</b></font> nullable;
    }
   
    <font color=#000099><b>public</b></font> <font color=#000099><b>void</b></font> <b>setMinimum</b>(Value minimum) {
        <font color=#000099><b>this</b></font>.minimum = minimum;
    }
   
    <font color=#000099><b>public</b></font> Value <b>getMinimum</b>() {
        <font color=#000099><b>return</b></font> minimum;
    }
   
    <font color=#000099><b>public</b></font> <font color=#000099><b>void</b></font> <b>setMaximum</b>(Value maximum) {
        <font color=#000099><b>this</b></font>.maximum = maximum;
    }
   
    <font color=#000099><b>public</b></font> Value <b>getMaximum</b>() {
        <font color=#000099><b>return</b></font> maximum;
    }   
}

Now we can very easily create validators for specific types, which are effectively
type aliases, as follows.

<font color=#000099><b>class</b></font> IntegerRangeValidator <font color=#000099><b>extends</b></font> ComparableRangeValidator&lt;Integer&gt; {    
}

Supoib!

Now let's check the PropertyDescriptor's of our minty IntegerRangeValidator.

<font color=#000099><b>public</b></font> <font color=#000099><b>class</b></font> IntegerRangeValidatorInsanityCheck {
    <font color=#000099><b>public</b></font> <font color=#000099><b>static</b></font> <font color=#000099><b>void</b></font> <b>main</b>(String[] args) <font color=#000099><b>throws</b></font> Exception {
        BeanInfo beanInfo = Introspector.<b>getBeanInfo</b>(IntegerRangeValidator.<font color=#000099><b>class</b></font>);
        <font color=#000099><b>for</b></font> (PropertyDescriptor property : beanInfo.<b>getPropertyDescriptors</b>()) {
            logger.<b>info</b>(property.<b>getName</b>(), property.<b>getPropertyType</b>());
        }
    }
}
INFO:logger:IntegerRangeValidator:main:123: (String) class :: (Class) Class
INFO:logger:IntegerRangeValidator:main:123: (String) exclusive :: (Class) boolean
INFO:logger:IntegerRangeValidator:main:123: (String) maximum :: (Class) Comparable
INFO:logger:IntegerRangeValidator:main:123: (String) minimum :: (Class) Comparable
INFO:logger:IntegerRangeValidator:main:123: (String) nullable :: (Class) boolean

Darn, the type of minimum and maximum is Comparable, as
in the generic ComparableRangeValidator superclass. Houston, why can't it just be Integer like i want?! Cos i wanna coerce (integer) values in there from string resources, so i'm stumped!