/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/event/EventDispatcher.java,v 1.7 2005/03/01 15:37:12 luetzkendorf Exp $
 * $Revision: 1.7 $
 * $Date: 2005/03/01 15:37:12 $
 *
 * ====================================================================
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.slide.event;

import org.apache.slide.util.conf.Configurable;
import org.apache.slide.util.conf.Configuration;
import org.apache.slide.util.conf.ConfigurationException;

import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Event dispatcher class
 *
 * @version $Revision: 1.7 $
 */
public class EventDispatcher implements Configurable {
    private static EventDispatcher eventDispatcher = new EventDispatcher();

    private List eventListeners = new ArrayList();

    private EventDispatcher() {
    }

    public static EventDispatcher getInstance() {
        return eventDispatcher;
    }

    public void addEventListener(EventListener listener) {
            eventListeners.add(listener);
    }

    public void fireVetoableEvent(VetoableEventMethod eventMethod, EventObject event) throws VetoException {
        for (Iterator i = eventListeners.iterator(); i.hasNext(); ) {
            EventListener listener = (EventListener)i.next();
            if ( listener instanceof GlobalListener ) {
                ((GlobalListener)listener).vetoableEventFired(eventMethod, event);
            } else {
                eventMethod.fireVetaoableEvent(listener, event);
            }
        }
    }

    public void fireEvent(EventMethod eventMethod, EventObject event) {
        for (Iterator i = eventListeners.iterator(); i.hasNext(); ) {
            EventListener listener = (EventListener)i.next();
            if ( listener instanceof GlobalListener ) {
                ((GlobalListener)listener).eventFired(eventMethod, event);
            } else {
                eventMethod.fireEvent(listener, event);
            }
        }
    }

    public void configure(Configuration config) throws ConfigurationException {
        Enumeration enableConfigs = config.getConfigurations("event");
        while (enableConfigs.hasMoreElements()) {
            Configuration enableConfig = (Configuration)enableConfigs.nextElement();
            String classname = enableConfig.getAttribute("classname");
            String method = enableConfig.getAttribute("method", null);
            boolean enable = enableConfig.getAttributeAsBoolean("enable", true);
            try {
                Class eventClass = Class.forName(classname);
                Field methodsField = eventClass.getField("methods");
                AbstractEventMethod[] methods = (AbstractEventMethod [])methodsField.get(null);
                for ( int i = 0; i < methods.length; i++ ) {
                    if ( method == null || methods[i].getName().equals(method) ) {
                        methods[i].setEnabled(enable);
                    }
                }
            } catch (NoSuchFieldException e) {
                throw new ConfigurationException("Event '"+classname+"' does not provide the required static member 'methods'", config);
            } catch (Exception e) {
                throw new ConfigurationException("Event '"+classname+"' could not be loaded (" + e + ")", config);
            }
        }
        
        Enumeration listenerConfigs = config.getConfigurations("listener");
        while (listenerConfigs.hasMoreElements()) {
            Configuration listenerConfig = (Configuration)listenerConfigs.nextElement();
            String classname = listenerConfig.getAttribute("classname");
            try {
                Class listenerClass = Class.forName(classname);
                EventListener eventListener = null;
                try {
                    Method getInstanceMethod = listenerClass.getMethod("getInstance", new Class[0]);
                    eventListener = (EventListener)getInstanceMethod.invoke(null, null);
                } catch ( NoSuchMethodException e) {
                    eventListener = (EventListener)listenerClass.newInstance();
                }
                if ( eventListener instanceof Configurable ) {
                   Configuration conf = null;
                   try {
                      conf = listenerConfig.getConfiguration("configuration");
                   } catch (ConfigurationException e) {
                      // ignore, listener has no configuration element, OK too
                   }
                   if (conf != null) {
                      ((Configurable)eventListener).configure(conf);
                   }
                }
                addEventListener(eventListener);
            } catch (ClassCastException e) {
                throw new ConfigurationException("Event listener '"+classname+"' is not of type EventListener", config);
            } catch (Exception e) {
                throw new ConfigurationException("Event listener '"+classname+"' could not be loaded (" + e + ")", config);
            }
        }
    }
}