/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999, 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.apache.tools.ant.gui.event;

import java.util.*;
import javax.swing.SwingUtilities;
/**
 * An event "bus" providing a centralized place for posting
 * and recieving generic application events. To receive events a class must 
 * implement the "BusMember" interface. When registering as a member, an
 * "interrupt level" is provided, which specifies a relative ordering level
 * that the member wishes to receive events for. By convention, a member
 * can be registered at the MONITORING, VETOING, or RESPONDING levels, which
 * correspond to recieving events first to receiving events last. If a member
 * receives an event, the event is of type AntEvent, and the member calls the 
 * AntEvent.cancel() method, the event is not then delivered
 * to subsequent members. Members also indicate interest in an event
 * by providing an instance of the BusFilter interface.<BR>
 *
 * NB: This class is overly simple right now, but will eventually
 * be expanded to do better event filtering, interrupt levels, etc. 
 * 
 * @version $Revision$ 
 * @author Simeon Fitch 
*/
public class EventBus {
	/** The default "vetoing" interrupt level, used by bus members
	 *  whose role is to veto request events or otherwise handle an
	 *  event before it is processed by the default handler. */
	public static final int VETOING = 1;
	/** The default "monitoring" interrupt level, used by members who
	 * are only listeners/monitors of events. */
	public static final int MONITORING = 5;
	/** The default "responding" interrupt level, for members who service
	 *  events in a default manner. */
	public static final int RESPONDING = 10;

	/** The maximum valid interrupt value. */
	public static final int MAX_INTERRUPT = 15;

    /** Set of bus members, with a list for each interrupt level. */
    private List[] _memberSet = new List[MAX_INTERRUPT];

	/** 
	 * Default ctor.
	 * 
	 */
    public EventBus() {
    }

	/** 
	 * Add a member to the bus. 
	 * 
	 * @param intLevel Interrupt level.
	 * @param member Member to add.
	 */
    public void addMember(int intLevel, BusMember member) {
		if(intLevel < 1 || intLevel > MAX_INTERRUPT) {
			throw new IllegalArgumentException(
				"Invalid interrupt level: " + intLevel);
		}
        synchronized(_memberSet) {
			List list = _memberSet[intLevel - 1];
			if(list == null) {
				list = new LinkedList();
				_memberSet[intLevel - 1] = list;
			}
            list.add(member);
        }
    }


	/** 
	 * Remove a member from the bus.
	 * 
	 * @param member Member to remove.
	 */
    public void removeMember(BusMember member) {
        synchronized(_memberSet) {
			// XXX lets hope we don't do too much removing. Yuck...
			for(int i = 0; i < _memberSet.length; i++) {
				if(_memberSet[i] == null) continue;
				_memberSet[i].remove(member);
			}
        }
    }

	/** 
	 * Method used for sending an event to the bus.
	 * 
	 * @param event Event to post.
	 */
    public void postEvent(EventObject event) {
        EventDispatcher disp = new EventDispatcher(event);

        // Events need to be dispatched on the AWTEvent thread, as the UI
        // components assume that.
        if(SwingUtilities.isEventDispatchThread()) {
            disp.run();
        }
        else {
            SwingUtilities.invokeLater(disp);
        }
    }

    /** Class that performs the duty of dispatching events to the members. */
    private class EventDispatcher implements Runnable {
        /** Event to dispatch. */
        private EventObject _event = null;
        
        /** 
         * Standard ctor.
         * 
         * @param event Event to dispatch.
         */
        public EventDispatcher(EventObject event) {
            _event = event;
        }

        /** 
         * Perform dispatching.
         * 
         */
        public void run() {
            synchronized(_memberSet) {
              outerLoop:
                for(int i = 0; i < _memberSet.length; i++) {
                    if(_memberSet[i] == null) continue;
                    
                    Iterator it = _memberSet[i].iterator();
                    while(it.hasNext()) {
                        BusMember next = (BusMember) it.next();
                        BusFilter filter = next.getBusFilter();
                        if(filter == null || filter.accept(_event)) {
                            // If false then callee canceled the event
                            // propogation.
                            if(!next.eventPosted(_event)) {
                                break outerLoop;
                            }
                        }
                    }
                }
            }
        }
    }
}
