/*********************************************************************** * mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***********************************************************************/ package org.mt4j.components; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * The Class StateChangeSupport. * A helper class to delegate state change event handling and dispatching to. * */ public class StateChangeSupport { /** The source. */ private Object source; /** The states to listener. */ private Map<StateChange, StateChangeListener[]> statesToListener; /** The Constant EMPTY. */ private static final StateChangeListener[] EMPTY = {}; /** * Constructs a <code>PropertyChangeSupport</code> object. * * @param sourceBean The bean to be given as the source for any events. */ public StateChangeSupport(Object sourceBean) { if (sourceBean == null) { throw new NullPointerException(); } source = sourceBean; } /** * Add a StateChangeListener to the listener map. * If <code>listener</code> is null, no exception is thrown and no action * is taken. * * @param state the state * @param listener The StateChangeListener to be added */ public synchronized void addStateChangeListener(StateChange state, StateChangeListener listener) { if (listener == null) { return; } this.lazyInitMap(); StateChangeListener[] array = this.statesToListener.get(state); //Add listener to array int size = (array != null) ? array.length : 0; StateChangeListener[] clone = newArray(size + 1); clone[size] = listener; if (array != null) { System.arraycopy(array, 0, clone, 0, size); } //Put new listener array into map this.statesToListener.put(state, clone); } /** * Creates a new array of listeners of the specified size. * * @param length the length * * @return the state change listener[] */ protected StateChangeListener[] newArray(int length) { return (0 < length) ? new StateChangeListener[length] : EMPTY; } /** * Removes a StateChangeListener to the listener map. * Throws no error if the listener isnt found. * * @param state the state * @param listener the listener */ public synchronized void removeStateChangeListener(StateChange state, StateChangeListener listener) { if (listener == null || state == null) { return; } this.lazyInitMap(); if (this.statesToListener != null) { StateChangeListener[] array = this.statesToListener.get(state); if (array != null) { for (int i = 0; i < array.length; i++) { if (listener.equals(array[i])) { int size = array.length - 1; if (size > 0) { StateChangeListener[] clone = newArray(size); System.arraycopy(array, 0, clone, 0, i); System.arraycopy(array, i + 1, clone, i, size - i); this.statesToListener.put(state, clone); } else { this.statesToListener.remove(state); if (this.statesToListener.isEmpty()) { this.statesToListener = null; } } break; } } } } } /** * Fire state change. * * @param state the state that has changed */ public void fireStateChange(StateChange state) { if (this.statesToListener != null && !this.statesToListener.isEmpty() ){ this.fireStateChange(new StateChangeEvent(source, state)); } } /** * Fire an existing StateChangeEvent to any registered listeners. * * @param evt The StateChangeEvent object. */ public void fireStateChange(StateChangeEvent evt) { if (this.statesToListener != null && !this.statesToListener.isEmpty() ){ StateChange stateName = evt.getState(); StateChangeListener[] common = this.statesToListener.get(null); StateChangeListener[] named = (stateName != null) ? this.statesToListener.get(stateName) : null; this.fire(common, evt); this.fire(named, evt); } } /** * Fires the events to the listeners. * * @param listeners the listeners * @param event the event */ private void fire(StateChangeListener[] listeners, StateChangeEvent event) { if (listeners != null) { for (StateChangeListener listener : listeners) { listener.stateChanged(event); } } } /** * Returns all listeners in the map. * * @return an array of all listeners */ public final synchronized StateChangeListener[] getListeners() { if (this.statesToListener == null) { return newArray(0); } List<StateChangeListener> list = new ArrayList<StateChangeListener>(); StateChangeListener[] listeners = this.statesToListener.get(null); if (listeners != null) { for (StateChangeListener listener : listeners) { list.add(listener); } } for (Entry<StateChange, StateChangeListener[]> entry : this.statesToListener.entrySet()) { StateChange state = entry.getKey(); if (state != null) { for (StateChangeListener listener : entry.getValue()) { list.add(listener); } } } return list.toArray(newArray(list.size())); } /** * Returns listeners that have been associated with the named state. * * @param state the name of the property * * @return an array of listeners for the named property */ public final StateChangeListener[] getListeners(StateChange state) { if (state != null) { StateChangeListener[] listeners = this.statesToListener.get(state); if (listeners != null) { return listeners.clone(); } } return newArray(0); } /** * Indicates whether the map contains * at least one listener to be notified. * * @param state the state * * @return {@code true} if at least one listener exists or * {@code false} otherwise */ public final synchronized boolean hasListeners(StateChange state) { if (this.statesToListener == null) { return false; } StateChangeListener[] array = this.statesToListener.get(null); return (array != null) || ((state != null) && (null != this.statesToListener.get(state))); } /** * Checks if the map is null and then lazily initializes it. */ private void lazyInitMap(){ if (statesToListener == null){ statesToListener = new HashMap<StateChange, StateChangeListener[]>(); } } }