package net.xoetrope.xui.registry;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.TransferHandler;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.swing.XList;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.build.BuildProperties;
/**
* A factory for transfer handlers
* <p>Copyright (c) Xoetrope Ltd., 1998-2006. See license.txt for more details</p>
*/
public abstract class XRegistrationFactory
{
/**
* Match transfer handlers based on the target component class name
*/
public static final int CLASS_MATCH = 0;
/**
* Match transfer handlers based on implementation of a particular interface
*/
public static final int INTERFACE_MATCH = 1;
/**
* Match transfer handlers based on inheritance from a particular class
*/
public static final int INSTANCE_MATCH = 2;
/**
* Match transfer handlers based on the results of a user defined comparison
*/
public static final int INSPECTOR_MATCH = 3;
private static final int NUM_MATCH_MODES = 4;
/**
* The register of transfer handler adapters for each component type
*/
protected ArrayList[] handlerRegisters;
/**
* 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;
private String factoryObjectName;
/**
* Used for tracking changes to the registry spec.
*/
protected int localChangeCounter = -1;
private XProject currentProject;
protected XRegistrationFactory( XProject project, String factoryName, String basicConfigFile, String startupKey, String defaultProjectConfigFile )
{
if ( BuildProperties.DEBUG )
DebugLogger.trace( "Setting up XTransferHandlerFactory" );
factoryObjectName = factoryName;
addConfigFile( "XUI", basicConfigFile, false );
currentProject = project;
currentProject.setObject( factoryName, this );
String handlerFile = currentProject.getStartupParam( startupKey );
if ( ( handlerFile == null ) || ( handlerFile.length() == 0 ) )
handlerFile = defaultProjectConfigFile;
if ( handlerFile.indexOf( ".xml" ) < 0 )
handlerFile += ".xml";
URL url = currentProject.findResource( handlerFile );
if ( url != null )
addConfigFile( "Project", url, false );
}
/**
* Get the name of this factory
* @return the factory name e.g. XRegisteredDataBindingFactory
*/
public abstract String getFactoryName();
/**
* Creates a new instance of TransferHandler for a particular type
* @param comp the source component
* @param instanceConfig the attributes of the instance
* @return the new transfer handler
*/
public Object getObject( Object comp, Hashtable instanceConfig )
{
checkRegistration();
String className = comp.getClass().getName();
for ( int mode = NUM_MATCH_MODES -1; mode >= 0; mode-- ) {
int numRegistrations = handlerRegisters[ mode ].size();
for ( int i = 0; i < numRegistrations; i++ ) {
XRegistrationEntry match = (XRegistrationEntry)handlerRegisters[ mode ].get( i );
if ( match.matchMode == mode ) {
if ( match.matches( mode, comp, match.registrationConfig, instanceConfig )) {
try {
XRegistrationSetup handler = (XRegistrationSetup)Class.forName( match.className.trim()).newInstance();
handler.setup( currentProject, comp, match.registrationConfig, instanceConfig );
return handler;
}
catch ( Exception ex )
{
ex.printStackTrace();
}
}
}
}
}
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()
{
handlerRegisters = new ArrayList[ NUM_MATCH_MODES ];
for ( int i = 0; i < NUM_MATCH_MODES; i++ )
handlerRegisters[ i ] = new ArrayList();
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( getFactoryName() + " 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( "XRegisteredDatatransfer handlerFactory 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 );
Vector registrationNodes = regRoot.getChildren();
int numElements = registrationNodes.size();
for ( int i = 0; i < numElements; i++ ) {
XmlElement regElement = (XmlElement)registrationNodes.elementAt( i );
String tag = regElement.getName();
int mode = ( tag.equals( "InspectorTransferHandlers" ) ? INSPECTOR_MATCH :
tag.equals( "ClassTransferHandlers" ) ? CLASS_MATCH :
tag.equals( "InterfaceTransferHandlers" ) ? INTERFACE_MATCH :
INSTANCE_MATCH
);
addRegistrationTypes( regElement, mode );
}
}
catch ( Exception ex ) {
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Unable to setup the component registry" );
}
}
/**
* Read the component registry. The format is described in the components.xsd
* schema.
* @param regRoot the name by which the configuration file is referenced
* @param mode the match mode for the new transfer handler types
*/
protected void addRegistrationTypes( XmlElement regRoot, int mode )
{
try {
Vector registrationNodes = regRoot.getChildren();
int numElements = registrationNodes.size();
for ( int i = 0; i < numElements; i++ ) {
XmlElement regElement = (XmlElement)registrationNodes.elementAt( i );
XRegistrationEntry registration;
if ( mode == INSPECTOR_MATCH ) {
registration = new XInspectorRegistrationEntry();
try {
String inspector = regElement.getAttribute( "inspector" );
if ( inspector != null ) {
Class inspectorClass = Class.forName( inspector );
((XInspectorRegistrationEntry)registration).inspector = (XInspector)inspectorClass.newInstance();
}
}
catch ( Exception ex )
{
ex.printStackTrace();
}
}
else
registration = new XRegistrationEntry();
registration.matchMode = mode;
registration.target = regElement.getAttribute( "target" );
registration.className = regElement.getAttribute( "class" );
handlerRegisters[ mode ].add( registration );
}
}
catch ( Exception ex ) {
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Unable to setup the component registry" );
}
}
/**
* Check that all the registered components are loaded
*/
public void checkRegistration()
{
if ( localChangeCounter != changeCounter ) {
read();
localChangeCounter = changeCounter;
}
}
}