package org.jcon.util;

import java.util.Hashtable;
import java.util.Vector;

/**
 * Your basic object Naming service. It behaves like
 * PropMap plus has event notification when an object
 * (aka property) has been changed.
 *
 * This class can be abused, just like global variables.
 * To avoid this problem, minimize the use of objects in
 * the Naming and document the setters, getters and
 * listeners.
 *
 * @author Jack Harich
 */
public class Naming extends PropMapStd {

//---------- Private Fields ------------------------------
// Key = propertyName, Object = Vector of NamingListener
private Hashtable props = new Hashtable();

//---------- Superclass Overrides ------------------------
public Object set(String key, Object newValue) {
    Object oldValue = get(key);
    super.set(key, newValue);
    if (! hasListener(key)) return oldValue;
    // Fire event
    NamingEvent evt = new NamingEvent(key, this);
    evt.setOldValue(oldValue);
    evt.setNewValue(newValue);
    if (oldValue == null) {
        evt.setEventType(NamingEvent.ADD);
    } else {
        evt.setEventType(NamingEvent.CHANGE);
    }
    sendEvent(evt);
    return oldValue;
}
public Object removeKey(String key) {
    Object oldValue = get(key);
    Object removedObject = super.removeKey(key);
    if (hasListener(key)) {
        // Fire event
        NamingEvent evt = new NamingEvent(key, this);
        evt.setOldValue(oldValue);
        evt.setEventType(NamingEvent.REMOVE);
        sendEvent(evt);
    }
    return removedObject;
}
//---------- Public Methods ------------------------------
/**
 * Adds a listener to changes to @param objectName.
 */
public synchronized void addListener(String objectName,
            NamingListener listener) {
    Vector listeners = (Vector)props.get(objectName);
    if (listeners == null) {
        listeners = new Vector();
        props.put(objectName, listeners);
    }
    listeners.addElement(listener);
}
/**
 * Listeners may stop listening to object changes.
 */
public synchronized void removeListener(String objectName,
        NamingListener listener) {

    Vector listeners = (Vector)props.get(objectName);
    if (listeners != null) {
        listeners.removeElement(listener);
        if (listeners.isEmpty()) {
            props.remove(objectName);
        }
    }
}
/**
 * Returns true if at least one listener is subscribed to
 * the @param objectName, false if none are subscribed.
 */
public synchronized boolean hasListener(String objectName) {
    return (props.get(objectName) != null ? true : false);
}
//---------- Private Methods -----------------------------
private void sendEvent(NamingEvent evt) {
    Vector listeners = (Vector)props.get(evt.getObjectName());
    if (listeners != null) {
        Vector list;
        synchronized(this) {
            list = (Vector)listeners.clone();
        }
        for (int i = 0; i < list.size(); i++) {
            NamingListener listener
                = (NamingListener)list.elementAt(i);
            listener.namingChange(evt);
        }
    }
}


} // End class
