package net.xoetrope.xui.validation;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.helper.ReflectionHelper;
import net.xoetrope.xui.style.XStyleEx;
/**
* <p>Constucts validations be reading rules from a configuration file</p>
* <p>Copyright: Copyright (c) Xoetrope Ltd., 1998-2003<br>
* License: see license.txt</p>
* $Revision: 2.4 $
*/
public class XValidationFactory
{
/**
* The config files used to build the set of component adapters. Stores the
* file names
*/
protected static Hashtable configFiles;
/**
* A counter for changes to the registry. Used to indicate if the registry
* needs to be rebuilt
*/
protected static int changeCounter;
/**
* Used for tracking changes to the registry spec.
*/
protected int localChangeCounter = -1;
protected XProject currentProject;
/**
* Hashtable of validation generated from the file referenced by the 'Validations' property in startup.properties
*/
protected Hashtable validations;
/**
* Constructor which reads validations from the reader
*/
public XValidationFactory( XProject project )
{
currentProject = project;
if ( BuildProperties.DEBUG )
DebugLogger.trace( "Setting up XValidationFactory" );
addConfigFile( "XUI", "net/xoetrope/xui/validation/validations.xml", false );
currentProject = project;
String projectValidationFile = currentProject.getStartupParam( "Validations" );
if ( ( projectValidationFile == null ) || ( projectValidationFile.length() == 0 ) )
projectValidationFile = "validations.xml";
if ( projectValidationFile.indexOf( ".xml" ) < 0 )
projectValidationFile += ".xml";
URL url = currentProject.findResource( projectValidationFile );
if ( url != null )
addConfigFile( "Project", url, false );
}
/**
* Creates an XValidation object which validates against a value returned from
* a function call
* @param validationName The name of the validation
* @param m The Method to call in order to get the value to validate against
* @param mask The event mask to be applied to the validation
* @param page The page object which contains the function
* @param pageEle the XML element which is declared in the page
* @return The new XValidator
*/
public XValidator getValidation( String validationName, Method m, int mask, Object page, XmlElement pageEle )
{
checkRegistration();
XValidator validator = getValidation( validationName, mask, page );
if ( validator != null )
validator.setValidationMethod( m, page );
return validator;
}
/**
* Gets the validation for the validationName. Checks first to see if the
* validation is a predefined type in which case it reads it's required
* attributes. If the validation type is custom we just need to create a
* validator from the class attribute
* @return The new XValidator
* @param page The page object which contains the function
* @param validationName The name of the validation
* @param mask The event mask to be applied to the validation
*/
public XValidator getValidation( String validationName, int mask, Object page )
{
XmlElement element = (XmlElement)validations.get( validationName );
if ( BuildProperties.DEBUG ) {
if ( element == null ) {
DebugLogger.logError( "Cannot find the validation rule: " + validationName );
return null;
}
}
String type = element.getAttribute( "type" );
String className = null;
if ( type.equals( "minmax" ))
className = "net.xoetrope.xui.validation.XMinMaxValidator";
else if ( type.equals( "mandatory" ))
className = "net.xoetrope.xui.validation.XMandatoryValidator";// mandatoryValidator = new XMandatoryValidator( currentProject, validationName, mask );
else if ( type.equals( "custom" ))
className = element.getAttribute( "class" );
if ( className != null ) {
XBaseValidator validator = null;
try {
Object[] values = new Object[ 1 ];
values[ 0 ] = currentProject;
validator = ( XBaseValidator )ReflectionHelper.constructViaReflection( className, values );
validator.setName( validationName );
validator.setMask( mask );
validator.setup( element );
}
catch ( Exception ex ) {
ex.printStackTrace();
}
return validator;
}
return null;
}
/**
* Add a configuration file.
* If the files have already been loaded then the new file will be loaded
* @param key the name by which the configuration file is referenced
* @param resource the name/path of the configuration file or the URL for the file
* @param overwrite true to overwrite and existing entry matching the specified key
*/
public static void addConfigFile( String key, Object resource, boolean overwrite )
{
if ( resource == null )
return;
if ( configFiles == null )
configFiles = new Hashtable();
Object resourceObj = configFiles.get( key );
// If the same file name is already used then ignore it.
if (( resourceObj != null ) && resource.equals( resourceObj ))
return;
changeCounter++;
if (( resourceObj != null ) || overwrite )
configFiles.remove( key );
configFiles.put( key, resource );
}
/**
* Signal that the configuration has been updated.
*/
public void updateConfig()
{
changeCounter++;
}
/**
* Read the component registry. The format is described in the components.xsd
* schema.
*/
protected void read()
{
if ( validations != null )
validations.clear();
Enumeration enumeration = configFiles.keys();
while ( enumeration.hasMoreElements() ) {
String key = ( String ) enumeration.nextElement();
if ( !key.equals( "Project" ) )
doRead( key, configFiles.get( key ) );
}
doRead( "Project", configFiles.get( "Project" ));
}
/**
* Read the component registry. The format is described in the components.xsd
* schema. The config file is also registered.
* @param configFile the name of the configuration file
* @param key the name by which the configuration file is referenced
*/
protected void read( String key, String configFile )
{
addConfigFile( key, configFile, true );
doRead( key, configFile );
}
/**
* Read the component registry. The format is described in the components.xsd
* schema.
* @param configFile the name of the configuration file
* @param key the name by which the configuration file is referenced
*/
protected void doRead( String key, Object configFile )
{
if ( configFile == null )
return;
else if ( configFile instanceof URL )
doRead( key, (URL)configFile );
else
doRead( key, (String)configFile );
}
/**
* Read the component registry. The format is described in the components.xsd
* schema.
* @param configFile the name of the configuration file
* @param key the name by which the configuration file is referenced
*/
protected void doRead( String key, String configFile )
{
if ( BuildProperties.DEBUG )
DebugLogger.trace( "XRegisteredDataBindingFactory reading config file: " + configFile );
try {
String fileName = configFile;
if ( fileName.indexOf( ".xml" ) < 0 )
fileName += ".xml";
Reader r = currentProject.getBufferedReader( fileName, null );
if ( r != null )
read( key, r );
}
catch ( Exception ex ) {
ex.printStackTrace();
}
}
/**
* Read the component registry. The format is described in the components.xsd
* schema.
* @param configFileURL the URL of the configuration file
* @param key the name by which the configuration file is referenced
*/
protected void doRead( String key, URL configFileURL )
{
if ( BuildProperties.DEBUG )
DebugLogger.trace( "XRegisteredDataBindingFactory reading config file: " + configFileURL.toString() );
try {
//String file = configFileURL.getFile();
Reader r = new BufferedReader( new InputStreamReader( configFileURL.openStream() ));
if ( r != null )
read( key, r );
}
catch ( Exception ex ) {
ex.printStackTrace();
}
}
/**
* Read the component registry. The format is described in the components.xsd
* schema.
* @param key the name by which the configuration file is referenced
* @param reader the reader from which to read the file
*/
public void read( String key, Reader reader )
{
try {
XmlElement regRoot = XmlSource.read( reader );
String validationStyle = regRoot.getAttribute( "style" );
if ( validationStyle != null ) {
XStyleEx exStyle = (XStyleEx)currentProject.getStyleManager().getStyle( validationStyle );
Color[] colors = new Color[ 3 ];
colors[ 0 ] = (Color)exStyle.getStyleAsColor( "colornormal" );
colors[ 1 ] = (Color)exStyle.getStyleAsColor( "colorwarn" );
colors[ 2 ] = (Color)exStyle.getStyleAsColor( "colorfail" );
XBaseValidator.setValidationColors( colors );
}
Enumeration e = regRoot.getChildren().elements();
validations = new Hashtable();
while ( e.hasMoreElements() ) {
XmlElement ele = (XmlElement)e.nextElement();
String name = ele.getAttribute( "name" );
validations.put( name, ele );
}
}
catch ( Exception ex ) {
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Unable to setup the validation registry" );
}
}
/**
* Loads XmlElements from the file and puts the element into the validations
* Hashtable with the name of the validation as the key.
* @param read The Reader object from which the validations will be loaded
*/
/**
* Check that all the registered components are loaded
*/
public void checkRegistration()
{
if ( localChangeCounter != changeCounter ) {
read();
localChangeCounter = changeCounter;
}
}
}