// RegistryModel.java
//
// Created: September 2005
//
// This file is a component of the BioMoby project.
// Copyright Martin Senger (martin.senger@gmail.com).
//

package org.biomoby.service.dashboard;

import org.biomoby.shared.MobyException;
import org.biomoby.shared.PendingCurationException;
import org.biomoby.shared.NoSuccessException;
import org.biomoby.shared.MobyDataType;
import org.biomoby.shared.MobyServiceType;
import org.biomoby.shared.MobyService;
import org.biomoby.shared.MobyNamespace;
import org.biomoby.shared.CentralAll;
import org.biomoby.client.CentralDigestCachedImpl;

import org.biomoby.shared.event.Notifier;
import org.biomoby.shared.event.NotificationEvent;
import org.biomoby.shared.event.NotificationListener;

import org.tulsoft.shared.FileUtils;
import org.tulsoft.shared.GException;

import org.apache.commons.lang.ClassUtils;

import java.util.Set;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.regex.Pattern;
import java.io.File;
import java.io.IOException;

/**
 * A real worker that supplies all data from a Biomoby registry (or
 * from a local cache) to various panels, especially to the {@link
 * RegistryPanel} and the {@link RegistrationPanel}. <p>
 *
 * @author <A HREF="mailto:martin.senger@gmail.com">Martin Senger</A>
 * @version $Id: RegistryModel.java,v 1.21 2008/03/02 12:45:26 senger Exp $
 */

public class RegistryModel
    extends AbstractModel
    implements Notifier {

    private static org.apache.commons.logging.Log log =
       org.apache.commons.logging.LogFactory.getLog (RegistryModel.class);

    // Biomoby parts: reflects the same what from is in
    // CentralDigestCachedImpl for cache parts - but here more
    // conveniently as integers
    public static final int PART_SERVICES      = 1;
    public static final int PART_DATA_TYPES    = 2;
    public static final int PART_SERVICE_TYPES = 4;
    public static final int PART_NAMESPACES    = 8;

    CentralAll worker;
    CentralDigestCachedImpl castWorker;

    boolean useCache = true;
    Hashtable dataTypesTable = new Hashtable();
    Hashtable serviceTypesTable = new Hashtable();
    Hashtable namespacesTable = new Hashtable();
    Hashtable servicesTable = new Hashtable();

    private static final String MSG_REG_PENDING_CURATION =
    "Registration reports 'Pending curation'...\n" +
    "Well, nobody knows what it means. Call Mark!";

    private static final String MSG_REG_NO_SUCCESS =
    "Registration reports 'No Success'...\n";

    private static final String MSG_UNREG_PENDING_CURATION =
    "Unregistration reports 'Pending curation'...\n" +
    "Well, nobody knows what it means. Call Mark!";

    private static final String MSG_UNREG_NO_SUCCESS =
    "Unregistration reports 'No Success'...\n";

    /*********************************************************************
     * Default constructor.
     ********************************************************************/
    public RegistryModel() {
	super();
    }

    /**************************************************************************
     * Return an endpoint (a stringified URL) of a default Moby
     * registry.
     *************************************************************************/
    public String getDefaultRegistryEndpoint() {
	return CentralDigestCachedImpl.DEFAULT_ENDPOINT;
    }

    /**************************************************************************
     * Return a namespace (a URI) used by a default Moby registry.
     *************************************************************************/
    public String getDefaultRegistryNamespace() {
	return CentralDigestCachedImpl.DEFAULT_NAMESPACE;
    }

    /**************************************************************************
     *
     *************************************************************************/
    public boolean setVerbose (boolean enabled) {
	try {
	    initWorker();
	    return worker.setDebug (enabled);
	} catch (Exception e) {
	    log.error ("Cannot set verbose mode: " + e.getMessage());
	    return false;
	}
    }

    /**************************************************************************
     * Call the given method (on worker) with data given in
     * 'inputXML'. Return whatever the worker's method returns.
     *************************************************************************/
    public String callRegistry (String methodName, File inputXML)
	throws MobyException {
	initWorker();
	try {
	    String xml = FileUtils.getFile (inputXML.getAbsolutePath(), false);
	    return worker.call (methodName, xml);
	} catch (IOException e) {
	    throw new MobyException ("Problem reading file. " + e.toString());
	} catch (GException e) {
	    throw new MobyException (e.getMessage());
	}
    }

    /*********************************************************************
     *
     * Dealing with Moby Data Types.
     *
     ********************************************************************/

    /*********************************************************************
     *
     ********************************************************************/
    public void registerDataType (MobyDataType dataType)
	throws MobyException {
	initWorker();
 	try {
	    worker.registerDataType (dataType);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_REG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_REG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public String getRegisterDataTypeXML (MobyDataType dataType)
	throws MobyException {
	initWorker();
	return ((CentralDigestCachedImpl)worker).getRegisterDataTypeXML (dataType);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void unRegisterDataType (MobyDataType dataType)
	throws MobyException {
	initWorker();
 	try {
	    worker.unregisterDataType (dataType);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_UNREG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_UNREG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void updateDataTypesCache()
	throws MobyException {
	initWorker();
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_DATATYPES);
	fireEvent (Notifier.DATA_TYPES_UPDATED, "", null);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void reloadDataTypesCache()
	throws MobyException {
	initWorker();
	castWorker.removeFromCache (CentralDigestCachedImpl.CACHE_PART_DATATYPES);
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_DATATYPES);
	fireEvent (Notifier.DATA_TYPES_UPDATED, "", null);
    }

    /*********************************************************************
     * Fetch data types (from a cache or from a registry). When done,
     * signal that data types are updated. If an initiator is known
     * (not null), signal, who asked for it, as well.
     ********************************************************************/
    public MobyDataType[] getDataTypes (Object initiator)
	throws MobyException {
	initWorker();
	if (initiator != null)
	    fireEvent (initiator, DATA_TYPES_RESET, "", null);
	MobyDataType[] dataTypes = worker.getDataTypes();
	dataTypesTable.clear();
	for (int i = 0; i < dataTypes.length; i++)
	    dataTypesTable.put (dataTypes[i].getName(), dataTypes[i]);
 	if (initiator != null)
 	    fireEvent (initiator, DATA_TYPES_UPDATED, "", dataTypes);
	return dataTypes;
    }

    /*********************************************************************
     *
     ********************************************************************/
    public MobyDataType getDataType (String dataTypeName)
	throws MobyException {
	if (dataTypeName == null)
	    return null;
	getDataTypes (null);
	return (MobyDataType)dataTypesTable.get (dataTypeName);
    }

    /*********************************************************************
     * Return a HashSet filled with names of data types that have
     * somewhere given 'searchText'. Add case-insensitivity to the
     * regular expression in 'searchText'.
     ********************************************************************/
    public HashSet findInDataTypes (String searchText)
	throws MobyException {
	HashSet found = new HashSet();
	MobyDataType[] dataTypes = getDataTypes (null);
	if (! searchText.startsWith ("(?i)"))
	    searchText = "(?i)" + searchText;
	Pattern pattern = Pattern.compile (searchText);
	for (int i = 0; i < dataTypes.length; i++) {
	    if (pattern.matcher (dataTypes[i].toString()).find())
		found.add (dataTypes[i].getName());
	}
	return found;
    }


    /*********************************************************************
     *
     * Dealing with Moby Service Types.
     *
     ********************************************************************/


    /*********************************************************************
     *
     ********************************************************************/
    public void registerServiceType (MobyServiceType serviceType)
	throws MobyException {
	initWorker();
 	try {
	    worker.registerServiceType (serviceType);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_REG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_REG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public String getRegisterServiceTypeXML (MobyServiceType serviceType)
	throws MobyException {
	initWorker();
	return ((CentralDigestCachedImpl)worker).getRegisterServiceTypeXML (serviceType);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void unRegisterServiceType (MobyServiceType serviceType)
	throws MobyException {
	initWorker();
 	try {
	    worker.unregisterServiceType (serviceType);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_UNREG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_UNREG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void updateServiceTypesCache()
	throws MobyException {
	initWorker();
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_SERVICETYPES);
	fireEvent (Notifier.SERVICE_TYPES_UPDATED, "", null);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void reloadServiceTypesCache()
	throws MobyException {
	initWorker();
	castWorker.removeFromCache (CentralDigestCachedImpl.CACHE_PART_SERVICETYPES);
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_SERVICETYPES);
	fireEvent (Notifier.SERVICE_TYPES_UPDATED, "", null);
    }

    /*********************************************************************
     * Fetch service types (from a cache or from a registry). When done,
     * signal that service types are updated. If an initiator is known
     * (not null), signal, who asked for it, as well.
     ********************************************************************/
    public MobyServiceType[] getServiceTypes (Object initiator)
	throws MobyException {
	initWorker();
	if (initiator != null)
	    fireEvent (initiator, SERVICE_TYPES_RESET, "", null);
	MobyServiceType[] serviceTypes = worker.getFullServiceTypes();
	serviceTypesTable = new Hashtable();
	for (int i = 0; i < serviceTypes.length; i++)
	    serviceTypesTable.put (serviceTypes[i].getName(), serviceTypes[i]);
 	if (initiator != null)
 	    fireEvent (initiator, SERVICE_TYPES_UPDATED, "", serviceTypes);
	return serviceTypes;
    }

    /*********************************************************************
     *
     ********************************************************************/
    public MobyServiceType getServiceType (String serviceTypeName)
	throws MobyException {
	if (serviceTypeName == null)
	    return null;
	getServiceTypes (null);
	return (MobyServiceType)serviceTypesTable.get (serviceTypeName);
    }

    /*********************************************************************
     * Return a Set filled with names of service types that have
     * somewhere given 'searchText'. Add case-insensitivity to the
     * regular expression in 'searchText'.
     ********************************************************************/
    public Set<String> findInServiceTypes (String searchText)
	throws MobyException {
	Set<String> found = new HashSet<String>();
	MobyServiceType[] serviceTypes = getServiceTypes (null);
	if (! searchText.startsWith ("(?i)"))
	    searchText = "(?i)" + searchText;
	Pattern pattern = Pattern.compile (searchText);
	for (int i = 0; i < serviceTypes.length; i++) {
	    if (pattern.matcher (serviceTypes[i].toString()).find())
		found.add (serviceTypes[i].getName());
	}
	return found;
    }


    /*********************************************************************
     *
     * Dealing with Moby Namespaces.
     *
     ********************************************************************/

    /*********************************************************************
     *
     ********************************************************************/
    public void registerNamespace (MobyNamespace namespace)
	throws MobyException {
	initWorker();
 	try {
	    worker.registerNamespace (namespace);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_REG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_REG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public String getRegisterNamespaceXML (MobyNamespace namespace)
	throws MobyException {
	initWorker();
	return ((CentralDigestCachedImpl)worker).getRegisterNamespaceXML (namespace);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void unRegisterNamespace (MobyNamespace namespace)
	throws MobyException {
	initWorker();
 	try {
	    worker.unregisterNamespace (namespace);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_UNREG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_UNREG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void updateNamespacesCache()
	throws MobyException {
	initWorker();
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_NAMESPACES);
	fireEvent (Notifier.NAMESPACES_UPDATED, "", null);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void reloadNamespacesCache()
	throws MobyException {
	initWorker();
	castWorker.removeFromCache (CentralDigestCachedImpl.CACHE_PART_NAMESPACES);
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_NAMESPACES);
	fireEvent (Notifier.NAMESPACES_UPDATED, "", null);
    }

    /*********************************************************************
     * Fetch namespaces (from a cache or from a registry). When done,
     * signal that namespaces are updated. If an initiator is known
     * (not null), signal, who asked for it, as well.
     ********************************************************************/
    public MobyNamespace[] getNamespaces (final Object initiator)
	throws MobyException {
	initWorker();
	if (initiator != null)
	    fireEvent (initiator, NAMESPACES_RESET, "", null);
	MobyNamespace[] namespaces = worker.getFullNamespaces();
	namespacesTable.clear();
	for (int i = 0; i < namespaces.length; i++)
	    namespacesTable.put (namespaces[i].getName(), namespaces[i]);
 	if (initiator != null)
 	    fireEvent (initiator, NAMESPACES_UPDATED, "", namespaces);
	return namespaces;
    }

    /*********************************************************************
     *
     ********************************************************************/
    public MobyNamespace getNamespace (String namespace)
	throws MobyException {
	if (namespace == null)
	    return null;
	getNamespaces (null);
	return (MobyNamespace)namespacesTable.get (namespace);
    }

    /*********************************************************************
     * Return a HashSet filled with names of namespaces that have
     * somewhere given 'searchText'. Add case-insensitivity to the
     * regular expression in 'searchText'.
     ********************************************************************/
    public HashSet findInNamespaces (String searchText)
	throws MobyException {
	HashSet found = new HashSet();
	MobyNamespace[] namespaces = getNamespaces (null);
	if (! searchText.startsWith ("(?i)"))
	    searchText = "(?i)" + searchText;
	Pattern pattern = Pattern.compile (searchText);
	for (int i = 0; i < namespaces.length; i++) {
	    if (pattern.matcher (namespaces[i].toString()).find())
		found.add (namespaces[i].getName());
	}
	return found;
    }


    /*********************************************************************
     *
     * Dealing with Moby Services.
     *
     ********************************************************************/


    /*********************************************************************
     *
     ********************************************************************/
    public void registerService (MobyService service)
	throws MobyException {
	initWorker();
 	try {
	    worker.registerService (service);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_REG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_REG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public String getRegisterServiceXML (MobyService service)
	throws MobyException {
	initWorker();
	return ((CentralDigestCachedImpl)worker).getRegisterServiceXML (service);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void unRegisterService (MobyService service)
	throws MobyException {
	initWorker();
 	try {
	    worker.unregisterService (service);
	} catch (PendingCurationException e) {
	    throw new MobyException (MSG_UNREG_PENDING_CURATION);
	} catch (NoSuccessException e) {
	    throw new MobyException (MSG_UNREG_NO_SUCCESS + e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void updateServicesCache()
	throws MobyException {
	initWorker();
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_SERVICES);
	fireEvent (Notifier.AUTHORITIES_UPDATED, "", null);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void reloadServicesCache()
	throws MobyException {
	initWorker();
	castWorker.removeFromCache (CentralDigestCachedImpl.CACHE_PART_SERVICES);
	castWorker.updateCache (CentralDigestCachedImpl.CACHE_PART_SERVICES);
	fireEvent (Notifier.AUTHORITIES_UPDATED, "", null);
    }

    /*********************************************************************
     * Fetch services (from a cache or from a registry). When done,
     * signal that services are updated. If an initiator is known (not
     * null), signal, who asked for it, as well.
     ********************************************************************/
    public MobyService[] getServices (Object initiator)
	throws MobyException {
	initWorker();
	if (initiator != null)
	    fireEvent (initiator, AUTHORITIES_RESET, "", null);
	MobyService[] services = worker.getServices();
	servicesTable = new Hashtable();
	for (int i = 0; i < services.length; i++)
	    servicesTable.put (services[i].getUniqueName(),
			       services[i]);
 	if (initiator != null)
 	    fireEvent (initiator, AUTHORITIES_UPDATED, "", services);
	return services;
    }

    /*********************************************************************
     *
     ********************************************************************/
    public MobyService getService (String combinedServiceName)
	throws MobyException {
	if (combinedServiceName == null)
	    return null;
	getServices (null);
	return (MobyService)servicesTable.get (combinedServiceName);
    }

    /*********************************************************************
     * Return a Set filled with names of services that have somewhere
     * given 'searchText'. Add case-insensitivity to the regular
     * expression in 'searchText'.
     ********************************************************************/
    public Set<String> findInServices (String searchText)
	throws MobyException {
	Set<String> found = new HashSet<String>();
	MobyService[] services = getServices (null);
	if (! searchText.startsWith ("(?i)"))
	    searchText = "(?i)" + searchText;
	Pattern pattern = Pattern.compile (searchText);
	for (int i = 0; i < services.length; i++) {
	    if (pattern.matcher (services[i].toString()).find())
		found.add (services[i].getUniqueName());
	}
	return found;
    }


    /*********************************************************************
     *
     * Dealing with local cache.
     *
     ********************************************************************/

    /*********************************************************************
     *
     ********************************************************************/
    protected String getCacheDir() {
	if (worker instanceof CentralDigestCachedImpl)
	    return ((CentralDigestCachedImpl)worker).getCacheDir();
	else
	    return null;
    }

    /*********************************************************************
     *
     ********************************************************************/
    public String getCacheInfoFormatted()
	throws MobyException {
	initWorker();
	StringBuffer buf = new StringBuffer (500);
	buf.append (castWorker.getCacheInfoFormatted (CentralDigestCachedImpl.CACHE_PART_SERVICES));
	buf.append (castWorker.getCacheInfoFormatted (CentralDigestCachedImpl.CACHE_PART_DATATYPES));
	buf.append (castWorker.getCacheInfoFormatted (CentralDigestCachedImpl.CACHE_PART_NAMESPACES));
	buf.append (castWorker.getCacheInfoFormatted (CentralDigestCachedImpl.CACHE_PART_SERVICETYPES));
	return new String (buf);
    }

    /*********************************************************************
     *
     * The rest...
     *
     ********************************************************************/

    /*********************************************************************
     *
     ********************************************************************/
    protected void initWorker()
	throws MobyException {
	initWorker (null);
    }

    /*********************************************************************
     *
     ********************************************************************/
    protected synchronized void initWorker (NotificationListener[] l)
	throws MobyException {

// 	if (log.isDebugEnabled()) {
// 	    StringBuffer buf = new StringBuffer();
// 	    StackTraceElement[] elems = new Throwable().getStackTrace();
// 	    for (int i = 0; i < Math.min (5, elems.length); i++) {
// 		buf.append ("\t");
// 		buf.append (elems[i].toString());
// 		buf.append ("\n");
// 	    }
// 	    log.debug ("Entering iniWorker:\n" + buf);
// 	}

	if (worker == null) {
	    String registryURL = propertyChannel.getString (DP_REGISTRY_ENDPOINT);
	    String registryNS = propertyChannel.getString (DP_REGISTRY_NAMESPACE);
	    String cacheDir = propertyChannel.getString (DP_CACHE_DIR);
	    useCache = propertyChannel.getBoolean (DP_USE_CACHE, true);
	    if (! useCache)
		cacheDir = null;
	    worker = new CentralDigestCachedImpl (registryURL,
						  registryNS,
						  cacheDir);
	} else {
	    String maybeNewRegistryURL = propertyChannel.getString (DP_REGISTRY_ENDPOINT);
	    String maybeNewRegistryNS = propertyChannel.getString (DP_REGISTRY_NAMESPACE);
	    String maybeNewCacheDir = propertyChannel.getString (DP_CACHE_DIR);
	    boolean maybeUseCache = propertyChannel.getBoolean (DP_USE_CACHE, true);

 	    if ( (maybeUseCache == useCache) && 
 		 maybeNewRegistryURL.equals (worker.getRegistryEndpoint()) &&
 		 maybeNewRegistryNS.equals (worker.getRegistryNamespace()) &&
 		 maybeNewCacheDir.equals (getCacheDir()) ) {
		if (l != null) {
		    ((Notifier)worker).addNotificationListeners (l);
		}
		return;
	    }
	    useCache = maybeUseCache;
	    if (! useCache)
		maybeNewCacheDir = null;
	    NotificationListener[] nls = ((Notifier)worker).getNotificationListeners();
	    ((Notifier)worker).removeNotificationListeners (nls);
	    worker = new CentralDigestCachedImpl (maybeNewRegistryURL,
						  maybeNewRegistryNS,
						  maybeNewCacheDir);
	    ((Notifier)worker).addNotificationListeners (nls);
	}
	if (l != null)
	    ((Notifier)worker).addNotificationListeners (l);
	castWorker = (CentralDigestCachedImpl)worker;
    }

    /*************************************************************************
     *
     * Methods implementing Notifier interface.
     *
     *************************************************************************/

    /*********************************************************************
     *
     ********************************************************************/
    public void addNotificationListener (NotificationListener l) {
	try {
	    initWorker (new NotificationListener[] { l });
// 	    ((Notifier)worker).addNotificationListener (l);
	    log.debug ("ADD listener: " + ClassUtils.getShortClassName (l, "unknown"));
	} catch (MobyException e) {
	    log.error (e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void addNotificationListeners (NotificationListener[] l) {
	try {
	    initWorker (l);
// 	    ((Notifier)worker).addNotificationListeners (l);
	} catch (MobyException e) {
	    log.error (e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public synchronized void removeNotificationListener (NotificationListener l) {
	try {
	    initWorker();
	    ((Notifier)worker).removeNotificationListener (l);
	    log.debug ("DEL listener: " + l.getClass().getName());
	} catch (MobyException e) {
	    log.error (e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void removeNotificationListeners (NotificationListener[] l) {
	try {
	    initWorker();
	    ((Notifier)worker).removeNotificationListeners (l);
	} catch (MobyException e) {
	    log.error (e.getMessage());
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public NotificationListener[] getNotificationListeners() {
	try {
	    initWorker();
	    return ((Notifier)worker).getNotificationListeners();
	} catch (MobyException e) {
	    log.error (e.getMessage());
	    return new NotificationListener[] {};
	}
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void callback (int signal) {
	if (worker != null)
	    ((Notifier)worker).callback (signal);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void fireEvent (int type, Object message, Object details) {
	if (worker != null)
	    ((Notifier)worker).fireEvent (type, message, details);
    }

    /*********************************************************************
     *
     ********************************************************************/
    public void fireEvent (NotificationEvent event) {
	if (worker != null)
	    ((Notifier)worker).fireEvent (event);
    }


    /*********************************************************************
     *
     ********************************************************************/
    protected void fireEvent (Object source,
			      int type, Object message, Object details) {
	NotificationEvent event = null;
	if (details == null)
	    event = new NotificationEvent (source, type, message);
	else
	    event = new NotificationEvent (source, type, message, details);
	fireEvent (event);
    }

}
