A quick cheat-sheet for custom constraints and validation
Keeping beans and their constraint metadata separate in source:
http://rifers.org/wiki/display/RIFE/Meta+data+merging
Adding constraints:
http://rifers.org/wiki/display/RIFE/Constraints
Validating:
http://rifers.org/wiki/display/RIFE/Validation
Adding custom constraints:
http://rifers.org/docs/api/com/uwyn/rife/site/ConstrainedProperty.html#constraint(java.lang.String,%20java.lang.Object)
And validating them (use/implement/extend as necessary):
http://rifers.org/docs/api/com/uwyn/rife/site/ValidationRule.html
Interfaces that may be interesting
http://rifers.org/docs/api/com/uwyn/rife/site/Validated.html
http://rifers.org/docs/api/com/uwyn/rife/site/ValidatedConstrained.html
A Custom Constraint and Validation Example
The following is an example bean and MetaData class using a custom validation rule. Our validation rule simply gives an error whenever an indicated field is the text "balrog".
package com.upbear.rife.creature;
public class Creature {
private int id = -1;
private String type = null;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
package com.upbear.rife.creature;
import com.uwyn.rife.site.AbstractTextualIdentifierGenerator;
import com.uwyn.rife.site.ConstrainedBean;
import com.uwyn.rife.site.ConstrainedProperty;
import com.uwyn.rife.site.MetaData;
public class CreatureMetaData extends MetaData {
public CreatureMetaData() { }
public void activateMetaData()
{
addConstraint(new ConstrainedBean()
.defaultOrder("type", ConstrainedBean.ASC)
.textualIdentifier(new AbstractTextualIdentifierGenerator<Creature>() {
public String generateIdentifier() {
return mBean.getType();
}
})
);
addConstraint(new ConstrainedProperty("id").editable(false).identifier(true));
addConstraint(new ConstrainedProperty("type").notNull(true).notEmpty(true).listed(true).maxLength(50));
addRule(new ValidationRuleNotBalrog("type"));
}
}
Note the use of Validated.addRule() above to have our custom validation rule check the property "type".
package com.upbear.rife.creature;
import java.lang.reflect.Array;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.uwyn.rife.site.ConstrainedProperty;
import com.uwyn.rife.site.ConstrainedUtils;
import com.uwyn.rife.site.PropertyValidationRule;
import com.uwyn.rife.site.ValidationError;
import com.uwyn.rife.tools.BeanUtils;
import com.uwyn.rife.tools.exceptions.BeanUtilsException;
public class ValidationRuleNotBalrog extends PropertyValidationRule {
Logger logger = Logger.getLogger(this.getClass().getName());
public ValidationRuleNotBalrog(String propertyName) {
super(propertyName);
}
public boolean validate()
{
Object value;
try
{
value = BeanUtils.getPropertyValue(getBean(), getPropertyName());
}
catch (BeanUtilsException e)
{
return true;
}
if (null == value)
{
return true;
}
ConstrainedProperty constrained_property = ConstrainedUtils.getConstrainedProperty(getBean(), getPropertyName());
if (value.getClass().isArray())
{
int length = Array.getLength(value);
for (int i = 0; i < length; i++)
{
if (isBalrog(BeanUtils.formatPropertyValue(Array.get(value, i), constrained_property)))
{
return false;
}
}
return true;
}
else
{
return !isBalrog(BeanUtils.formatPropertyValue(value, constrained_property));
}
}
public ValidationError getError()
{
ValidationError error = new ValidationError("BALROG", getSubject()) {};
return error;
}
private boolean isBalrog(String type) {
if( type != null && type.trim().toLowerCase().equals("balrog") ) {
return true;
}
return false;
}
}
Note that this validation rule handles arrays and any specified custom formats. You may not need this level of detail, but it does make the validation rule more generalized and reusable.
Also note our custom error identifier "BALROG". Anywhere you would use another identifier, such as "INVALID", or "MANDATORY", in your templates, you can use "BALROG" to provide feedback on this particular validation error. I've left code in for using "INVALID" if you'd like to reuse one of the existing identifiers.
Finally, note that ValidationError is an abstract class, so we cannot simply instantiate it for a new identifier, we have to subclass it – note the "{}" on the end of the "new ValidationError" statement.
...
<!--B 'BALROG:type'-->You shall not pass!!<br /><!--/B-->
...
And, there you have it – a custom validation rule with a custom error message!
Using custom validation with RIFE/CRUD
The above metadata and validation rule can be used as is with RIFE/CRUD to indicate errors in a submission. However, if you want to have RIFE/CRUD indicate an error more useful than the text "BALROG:type", then you'll need to override the default RIFE/CRUD template and create a resource properties file.
IMPORTANT: When creating a custom CRUD template, make sure that it is located in your classpath before the rife-crud-resources.jar; otherwise your custom template will never be loaded.
IMPORTANT: Note that the package for the templates is 'crud.common' and not 'templates.crud.common'.
<!--V 'error_area'--><!--/V-->
<!--B 'error_area'-->
<table summary="overview of errors" width="100%" class="error_table">
<cols>
<col width="100%" />
</cols>
<tr>
<td class="error_title">
<!--V 'error_title'/--><!--BV 'error_title'--><!--V 'L10N:error_area_title'-->error_area_title<!--/V--><!--/BV--><br>
</td>
</tr>
<tr>
<td>
<!--V 'error_content'--><!--/V-->
<!--B 'error_line'--><!--V 'error_message'--><!--/V--><br /><!--/B-->
</td>
</tr>
</table>
<br />
<!--/B-->
<!--V 'ERRORS:*'--><!--/V-->
<!--B 'ERRORS:'--><div class="form_error_area"><!--V 'ERRORS'/--></div><!--/B-->
<!--B 'ERRORS:*'--><!--V 'ERRORS'/--><!--/B-->
<!--B 'ERRORMESSAGE:*'--><!--V 'ERRORMESSAGE'/--><br /><!--/B-->
<!--B 'MARK:ERROR'-->form_field_error<!--/B-->
<!--B 'MANDATORY:*'--><!--V 'L10N:error_mandatory'-->error_mandatory<!--/V--><!--/B-->
<!--B 'WRONGLENGTH:*'--><!--V 'L10N:error_wronglength'-->error_wronglength<!--/V--><!--/B-->
<!--B 'NOTSAME:*'--><!--V 'L10N:error_notsame'-->error_notsame<!--/V--><!--/B-->
<!--B 'INVALID:*'--><!--V 'L10N:error_invalid'-->error_invalid<!--/V--><!--/B-->
<!--B 'UNICITY:*'--><!--V 'L10N:error_unicity'-->error_unicity<!--/V--><!--/B-->
<!--B 'NOTNUMERIC:*'--><!--V 'L10N:error_notnumeric'-->error_notnumeric<!--/V--><!--/B-->
<!--B 'BALROG:*'--><!--V 'L10N:error_balrog'-->error_balrog<!--/V--><!--/B-->
<!--B 'UNEXPECTED_ERROR'--><!--V 'L10N:error_unexpected'-->error_unexpected<!--/V--><!--/B-->
The only modification to the default error_area.html template is the addition of the 'BALROG' line.
error_area_title = The following errors occured
error_mandatory = Required
error_wronglength = Wrong length
error_notsame = Not the same
error_invalid = Invalid
error_unicity = Already exists
error_notnumeric = Not numeric
error_unexpected = Unexpected error
error_balrog = You shall not pass!!