Notifier.java

/**
 * VStar: a statistical analysis tool for variable star data.
 * Copyright (C) 2009  AAVSO (http://www.aavso.org/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */
package org.aavso.tools.vstar.util.notification;

import java.util.concurrent.CopyOnWriteArrayList;

/**
 * A notifier class genericised on the class of object that will be sent to
 * Listeners. This is a simpler, more flexible, and more type-safe form of the
 * Observer pattern than the one provided via the standard Java
 * Observer/Observable framework. A good candidate for T is an enum. Notice that
 * both notifier and listener must share the same type T.
 */
public class Notifier<T> {

	// The list of objects with an interest in the
	// notifier's activities.
	private CopyOnWriteArrayList<Listener<T>> listeners;

	// The list of messages for this notifier's listeners.
	private CopyOnWriteArrayList<T> messages;
	
	/**
	 * Constructor
	 */
	public Notifier() {
		this.listeners = new CopyOnWriteArrayList<Listener<T>>();
		this.messages = new CopyOnWriteArrayList<T>();
	}

	/**
	 * Add a listener with no immediate notification message.
	 * 
	 * @param listener
	 *            The listener to add.
	 */
	public void addListener(Listener<T> listener) {
		this.addListener(listener, false);
	}

	/**
	 * Add a listener, and specify whether to notify it immediately.
	 * 
	 * @param listener
	 *            The listener to add.
	 * @param immediateMessages
	 *            Send all messages so far to this new listener.
	 */
	public void addListener(Listener<T> listener, boolean immediateMessages) {
		listeners.addIfAbsent(listener);

		if (immediateMessages) {
			for (T message : messages) {
				listener.update(message);
			}
		}
	}

	/**
	 * Remove a listener, if it says it is willing to be removed.
	 * 
	 * @param listener
	 *            The listener to remove.
	 */
	public void removeListenerIfWilling(Listener<T> listener) {
		if (listener.canBeRemoved()) {
			listeners.remove(listener);
		}
	}

	/**
	 * Remove all listeners that are willing to be removed.
	 * Also, clear the message list.
	 */
	public void cleanup() {
		messages.clear();
		
		for (Listener<T> listener : listeners) {
			removeListenerIfWilling(listener);
		}
	}
	
	/**
	 * Notify all listeners of an activity update and collect the message
	 * for future replay, in particular for new listeners.
	 * 
	 * @param message
	 *            The message to pass to each listener.
	 */
	public void notifyListeners(T message) {
		messages.add(message);
		
		for (Listener<T> listener : listeners) {
			listener.update(message);
		}
	}
}