package net.xoetrope.xui.validation;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.FocusEvent;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xui.XEventHandler;
import net.xoetrope.xui.build.BuildProperties;
/**
* <p>Provides a means of managing validations. This class is
* intended as a mixin class for the panel classes such as XPage. Validations
* are integrated with the event handler so that when the event with which the
* validation is associated is triggered the validation is invoked. Failure of
* a validation results in subsequent event handler methods being blocked.</p>
* <p>Validations are also invoked at page transition and all validations rules
* are checked. If all validations are not passed then the page transition
* can be blocked.</p><p>If a validation failes then an exception is thrown.
* The exception is by default handled by this class but it can be redirected to
* a custom exception handler with the setExceptionHandler method. This interface
* allows exceptions to handled in a variety of different ways.</p>
* <p>Copyright: Copyright (c) Xoetrope Ltd., 1998-2003<br>
* License: see license.txt
* $Revision: 1.6 $
*/
public class XValidationHandler implements XValidationExceptionHandler
{
protected XValidationFactory validationFactory;
protected Hashtable validations;
protected XValidationExceptionHandler exceptionHandler;
protected XEventHandler eventHandler;
protected Container container;
/**
* Create a new validation handler for the specified container. The container
* provides the context in which the validation operate. Therefore validation
* methods are found by reflection in the specified container. Normally the
* container is the current XPage or a derived class.
* @param c the container
*/
public XValidationHandler( Container c )
{
exceptionHandler = this;
container = c;
eventHandler = null;
}
/**
* Set the event handler instance. This class needs to interact with the event
* handler to manage focus and to gain access to the current event
* @param eh the event handler
*/
public void setEventHandler( XEventHandler eh )
{
eventHandler = eh;
clearValidations();
eventHandler.addFocusHandler( container, "validationHandler" );
}
/**
* Set the validation exception handler called when a validation exception is trapped
* @param eh
*/
public void setExceptionHandler( XValidationExceptionHandler eh )
{
exceptionHandler = eh;
}
/**
* Reset/removes all validations
*/
public void clearValidations()
{
validations = new Hashtable( 5 );
}
/**
* Adds a validation to this page.
* @param comp the component being validated
* @param validationName the name of the validation in the validation file
* @param method the method used to get the component's value if any
* @param mask the event mask used to filter the events that trigger the validation
* @return the new and initialized XValidator
*/
public XValidator addValidation( Component comp, String validationName, String method, int mask )
{
XValidator validator = getValidation( validationName, method, mask );
Vector v = ( Vector )validations.get( new Long( FocusEvent.FOCUS_LOST * comp.hashCode() ) );
if ( v == null ) {
v = new Vector();
v.addElement( comp );
validations.put( new Long( FocusEvent.FOCUS_LOST * comp.hashCode() ), v );
switch ( mask ) {
case FocusEvent.FOCUS_GAINED:
case FocusEvent.FOCUS_LOST:
default:
eventHandler.addFocusHandler( comp, "validationHandler" );
break;
}
}
v.addElement( validator );
return validator;
}
/**
* Adds a validation to this page.
* @param comp the component being validated
* @param validationName the name of the validation in the validation file
* @param mask the event mask used to filter the events that trigger the validation
* @return the new and initialized XValidator
*/
public XValidator getValidation( String validationName, String method, int mask )
{
Method m = null;
if ( (method != null) && (method.length()>0) ) {
try {
m = container.getClass().getMethod( method, null );
}
catch ( Exception e ) {
e.printStackTrace();
}
}
if ( BuildProperties.DEBUG ) {
if ( validationFactory == null ) {
DebugLogger.logError( "No validation factory is set for the page. Cannot build the validation rule: " + validationName );
return null;
}
}
XValidator validator = validationFactory.getValidation( validationName, m, mask, container );
return validator;
}
/**
* Gets a XValidator object. The parameters of the object are read from the
* validation file
* @param validationName the name of the validation in the validation file
* @param method the method used to get the component's value if any
* @return the new and initialized XValidator
*/
public XValidator getValidation( String validationName, String method )
{
return getValidation( validationName, method, 0 );
}
/**
* Gets all the validations installed for a component
* @param comp the target component
* @return a Vector of XValidators
*/
public Vector getValidations( Component comp )
{
XValidator validator = null;
int ret = 0;
try {
// Get the event source
if ( eventHandler == null )
return null;
// Get the list of validators for the component
Vector v = ( Vector )validations.get( new Long( FocusEvent.FOCUS_LOST * comp.hashCode() ) );
if ( v != null )
return v;
}
catch ( Exception ex )
{
}
return null;
}
/**
* Adds a validation to this page. It is assumed that the validation will be
* invoked in response to FocusEvent.FOCUS_LOST events
* @param comp the component being validated
* @param validationName the name of the validation in the validation file
* @param method the method used to get the component's value if any
* @return the new and initialized XValidator
*/
public XValidator addValidation( Component comp, String validationName, String method )
{
return addValidation( comp, validationName, method, FocusEvent.FOCUS_LOST );
}
/**
* Adds a validation to this page. It is assumed that the validation will be
* invoked in response to FocusEvent.FOCUS_LOST events
* @param comp the component being validated
* @param validationName the name of the validation in the validation file
* @return the new and initialized XValidator
*/
public XValidator addValidation( Component comp, String validationName )
{
return addValidation( comp, validationName, null, FocusEvent.FOCUS_LOST );
}
/**
* Sets the factory used to create XValidator objects
* @param vf
*/
public void setValidationFactory( XValidationFactory vf )
{
validationFactory = vf;
}
/**
* Invoke the validators for the last event. Multiple validations are checked
* in the order in which they were added.
* @return the maximum level returned by the validators
*/
public int validationHandler()
{
Component comp = null;
XValidator validator = null;
int ret = 0;
try {
// Get the event source
comp = ( Component )eventHandler.getCurrentEvent().getSource();
// Get the list of validators for the component
Vector v = ( Vector )validations.get( new Long( FocusEvent.FOCUS_LOST * comp.hashCode() ) );
if ( v != null ) {
// Iterate over the list of validations
for ( int i = 1; i < v.size(); i++ ) {
validator = ( XValidator )v.elementAt( i );
if ( validator != null ) {
// Check that the validator is to be used for this event
if ( eventHandler.getCurrentEvent().getID() == validator.getMask() ) {
// Validate and get the maximum error level
ret = Math.max( ret, validator.getLevel() );
validator.validate( comp, false );
}
}
}
}
}
catch ( Exception ex ) {
exceptionHandler.handleException( comp, ex, validator );
return ret;
}
return XValidator.LEVEL_IGNORE;
}
/**
* Check all validations for this page. Typically this method should be
* invoked prior to a page transition or a critical transaction.
* @return the maximum error level raised by the validators
*/
public int checkValidations()
{
// Initialize the error level
int ret = XValidator.LEVEL_IGNORE;
// Iterate over all the validators
Enumeration e = validations.elements();
XValidator validator = null;
Component comp = null;
while ( e.hasMoreElements() ) {
Vector v = ( Vector )e.nextElement();
if ( v != null ) {
// Check the validators for each component
comp = ( Component )v.elementAt( 0 );
int numValidations = v.size();
for ( int i = 1; i < numValidations; i++ ) {
try {
validator = ( XValidator )v.elementAt( i );
if ( validator != null )
validator.validate( comp, true );
}
catch ( Exception ex ) {
// Record the maximum error level
ret = Math.max( ret, validator.getLevel() );
// Redirect error handling to the error handler.
exceptionHandler.handleException( comp, ex, validator );
}
}
}
}
return ret;
}
/**
* informs the handler when a page validation is starting or stopping. Typically
* when it starts the page will begin to accumulate messages which are to be displayed.
* When the parameter is false the page will usually display the accumulated
* messages
* @param start boolean to indicate whether the accumulation is started or stopped.
*/
public int accumulateMessages( boolean accumulate, int level )
{
if ( !exceptionHandler.equals(this) )
return exceptionHandler.accumulateMessages( accumulate, level );
return 0;
}
/**
* A method called when a validation exeption has been trapped.
*
* @param c Component being validated
* @param ex The exception caused
* @param validator The validator being used to validate.
* @return true to continue with error validation or false to suppress further
* validation.
*/
public boolean handleException( Component comp, Exception ex, XValidator validator )
{
ex.printStackTrace();
return true;
}
}
|