package net.xoetrope.xui.events;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.build.BuildProperties;
/**
* An action manager for applications. Actions can be added on an application
* wide basis or for a particular context, perhaps corresponding to a page, menu
* or toolbar. The actions can be turned on or off indvidually or according to
* the context.
* <p> Copyright (c) Xoetrope Ltd., 2002-2006. See license.txt for licensing
* details</p>
* @author luano
*/
public class XActionManager
{
private static XActionManager instance = null;
/**
* The register of binding adapters for each component type
*/
protected Hashtable actionContexts;
/**
* 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;
private XProject currentProject;
private XActionFactory actionFactory;
/** Creates a new instance of XActionManager */
private XActionManager( XProject project )
{
currentProject = project;
actionContexts = new Hashtable();
String projectRegistryFile = currentProject.getStartupParam( "Actions" );
if ( ( projectRegistryFile == null ) || ( projectRegistryFile.length() == 0 ) )
projectRegistryFile = "actions";
if ( projectRegistryFile.indexOf( ".xml" ) < 0 )
projectRegistryFile += ".xml";
URL url = currentProject.findResource( projectRegistryFile );
if ( url != null )
addConfigFile( "Project", url, false );
}
/**
* Register an instance of this binding factory.
* @param currentProject the owner project
*/
public static void register( XProject currentProject )
{
instance = (XActionManager)currentProject.getObject( "ActionManager" );
if ( instance == null )
instance = new XActionManager( currentProject );
currentProject.setObject( "ActionManager", instance );
}
/**
* Try to get a binding factory to construct the binding
* @param context the action context or null
* @param id the action id
* @return the new binding if one could be constructed
*/
public XAction getAction( String context, Object id )
{
checkRegistration();
if ( context != null ) {
Hashtable actions = (Hashtable)actionContexts.get( context );
if ( actions != null ) {
return (XAction)actions.get( id );
}
}
else {
Enumeration actionEnum = actionContexts.elements();
while ( actionEnum.hasMoreElements()) {
Hashtable actions = (Hashtable)actionContexts.get( actionEnum.nextElement());
XAction targetAction = (XAction)actions.get( id );
if ( targetAction != null )
return targetAction;
}
}
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()
{
actionContexts.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 );
// Setup the action factory
String actionFactoryClass = (String)regRoot.getAttribute( "factory" );
if ( actionFactoryClass == null )
actionFactoryClass = "net.xoetrope.swing.actions.XSwingActionFactory";
try {
if ( actionFactory == null )
actionFactory = (XActionFactory)Class.forName( actionFactoryClass.trim()).newInstance();
}
catch ( Exception ex )
{
DebugLogger.logError( "Unable to setup the action factory" );
ex.printStackTrace();
return;
}
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();
String context;
if ( !"Context".equalsIgnoreCase( tag )) {
context = "global";
Hashtable contextActions = (Hashtable)actionContexts.get( context );
if ( contextActions == null )
actionContexts.put( context, contextActions = new Hashtable());
contextActions.put( regElement.getAttribute( "id" ), actionFactory.getAction( regElement ));
}
else {
context = regElement.getAttribute( "name" );
Hashtable contextActions = (Hashtable)actionContexts.get( context );
if ( contextActions == null )
actionContexts.put( context, contextActions = new Hashtable());
Vector contextChildren = regElement.getChildren();
int numChildren = contextChildren.size();
for ( int j = 0; j < numChildren; j++ ) {
XmlElement actionElement = (XmlElement)contextChildren.elementAt( j );
contextActions.put( actionElement.getAttribute( "id" ), actionFactory.getAction( actionElement ));
}
}
}
}
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;
}
}
}