blob: 3bafecd919d40d6300c1ba966b64f765b08e9724 [file] [log] [blame]
package org.eclipse.jst.jsf.common.internal.resource;
import java.util.Collection;
import java.util.Collections;
import java.util.EventObject;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jst.jsf.common.JSFCommonPlugin;
/**
*
* @author cbateman
* @param <EVENTTYPE>
* @param <LISTENERTYPE>
* @param <LIFECYCLEOBJECT>
*
*/
public abstract class AbstractLifecycleListener<EVENTTYPE extends EventObject, LISTENERTYPE extends ILifecycleListener<EVENTTYPE>, LIFECYCLEOBJECT>
extends ImmutableLifecycleListener<LISTENERTYPE>
{
/**
* Exception (non-localized) error message thrown when a null object is
* added.
*/
protected static final String CANNOT_ADD_NULL_RESOURCE = "Cannot add null object"; //$NON-NLS-1$
static final boolean TRACE_EVENTS;
static
{
TRACE_EVENTS = Boolean.valueOf(
Platform.getDebugOption(JSFCommonPlugin.PLUGIN_ID
+ "/debug/lifecyclelistener")).booleanValue() //$NON-NLS-1$
|| Boolean.valueOf(System.getProperty("org.eclipse.jst.jsf.common/debug/lifecyclelistener")).booleanValue(); //$NON-NLS-1$
}
private final CopyOnWriteArrayList<LISTENERTYPE> _listeners = new CopyOnWriteArrayList<LISTENERTYPE>();
private final CopyOnWriteArrayList<LIFECYCLEOBJECT> _lifecycleObjects = new CopyOnWriteArrayList<LIFECYCLEOBJECT>();
private final AtomicBoolean _isDisposed = new AtomicBoolean(false);
/**
* Adds listener to the list of objects registered to receive lifecycle
* events for this resource. Only adds the listener if it is not already in
* the list.
*
* Method is thread-safe and may block the caller
*
* Throws {@link IllegalStateException} if isDisposed() == true Throws
* {@link NullPointerException} if listener == null
*
* @param listener
*/
@Override
public void addListener(final LISTENERTYPE listener)
{
if (isDisposed())
{
throw new IllegalStateException();
}
if (listener == null)
{
throw new NullPointerException("Cannot pass null listener"); //$NON-NLS-1$
}
_listeners.addIfAbsent(listener);
}
/**
* Removes listener from the list of registered listeners
*
* Method is thread-safe and may block the caller
*
* Throws {@link IllegalStateException} if isDisposed() == true
*
* @param listener
*/
@Override
public void removeListener(final LISTENERTYPE listener)
{
if (isDisposed())
{
throw new IllegalStateException();
}
_listeners.remove(listener);
}
/**
* @param event
*/
protected void fireLifecycleEvent(final EVENTTYPE event)
{
boolean disposeAfter = false;
if (TRACE_EVENTS)
{
System.err.println(event);
}
// NOTE: must use iterator through _listeners so that
// CopyOnWriteArrayList protects us from concurrent modification
for (final LISTENERTYPE listener : _listeners)
{
final EventResult result = listener.acceptEvent(event);
disposeAfter |= result.getDisposeAfterEvent();
}
if (disposeAfter)
{
dispose();
}
}
/**
* @return true if the listener has been disposed
*/
public boolean isDisposed()
{
return _isDisposed.get();
}
/**
* Release the resource change listener
*/
public final void dispose()
{
if (_isDisposed.compareAndSet(false, true))
{
// ensure that add/removeResource don't cause races to add or
// remove the resource listener
synchronized (_lifecycleObjects)
{
// remove first to minimize the chance that the listener will
// be triggered during the remainder of dispose
removeSystemChangeListener();
_lifecycleObjects.clear();
doDispose();
}
}
}
/**
* Sub-class disposal specialization.
*/
protected void doDispose()
{
// do nothing by default; sub-classes should override to provide their
// own disposal.
}
/**
* @param object
*/
protected void addLifecycleObject(final LIFECYCLEOBJECT object)
{
if (object == null)
{
throw new NullPointerException(CANNOT_ADD_NULL_RESOURCE);
}
synchronized (_lifecycleObjects)
{
// don't add any resources if we've disposed before acquiring the
// lock
if (isDisposed())
{
return;
}
final int preSize = _lifecycleObjects.size();
if (!_lifecycleObjects.contains(object))
{
_lifecycleObjects.add(object);
}
// if the size of the array was 0
// and is now greater, make sure the listener is added
if (preSize == 0 && _lifecycleObjects.size() > 0)
{
addSystemChangeListener();
}
}
}
/**
* If there are no longer resources being targeted, the resource change
* listener will be removed.
*
* @param res
*/
public void removeResource(final IResource res)
{
synchronized (_lifecycleObjects)
{
// don't bother with this stuff if we're disposed.
if (isDisposed())
{
return;
}
_lifecycleObjects.remove(res);
// if there are no longer target resources,
// remove the workspace listener
if (_lifecycleObjects.size() == 0)
{
removeSystemChangeListener();
}
}
}
/**
* @return an iterable of the lifecycle objects being tracked.
*/
protected Collection<LIFECYCLEOBJECT> getLifecycleObjects()
{
return Collections.unmodifiableCollection(_lifecycleObjects);
}
/**
* Add the system change listener that is used to collect events that will
* be processed into lifecycle change events.
*/
protected abstract void addSystemChangeListener();
/**
* Remove a system change listener added using addSystemChangeListener
*/
protected abstract void removeSystemChangeListener();
}