blob: 886a75cf59a818a02ad5bd7893270624cf869f42 [file] [log] [blame]
/*
* Copyright (c) 2010-2020 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
package org.eclipse.scout.sdk.core.util;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableCollection;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
import java.util.Iterator;
import java.util.List;
/**
* <h3>{@link EventListenerList}</h3>
* <p>
* List of {@link EventListener}s supporting weak listeners by implementing {@link IWeakEventListener}. This class is
* thread-safe.
*
* @since 7.0.0
*/
public final class EventListenerList {
private List<Object> m_listeners;
private final Object m_lock = new Object();
private List<Object> getOrCreateList() {
if (m_listeners == null) {
m_listeners = new ArrayList<>();
}
return m_listeners;
}
/**
* Removes all listeners.
*/
public void clear() {
synchronized (m_lock) {
if (m_listeners == null) {
return;
}
m_listeners.clear();
m_listeners = null;
}
}
/**
* Adds the specified listener. If the specified listener implements {@link IWeakEventListener} it is added weakly.
*
* @param listener
* The listener to add. Must not be {@code null}.
*/
public void add(EventListener listener) {
synchronized (m_lock) {
if (listener instanceof IWeakEventListener) {
getOrCreateList().add(new WeakReference<>(listener));
}
else {
getOrCreateList().add(Ensure.notNull(listener));
}
}
}
/**
* @return Gets all {@link EventListener}s as {@link Collection} of {@link EventListener}s.
*/
public Collection<EventListener> get() {
return get(EventListener.class);
}
/**
* Gets all listeners of the specified type.
*
* @param type
* The type or {@code null} if all should be returned.
* @return The matching listeners as unmodifiable {@link Collection}.
*/
@SuppressWarnings("unchecked")
public <T extends EventListener> Collection<T> get(Class<T> type) {
Collection<Object> candidates;
synchronized (m_lock) {
if (m_listeners == null) {
return emptyList();
}
candidates = new ArrayList<>(m_listeners.size());
Iterator<Object> iterator = m_listeners.iterator();
while (iterator.hasNext()) {
Object entry = iterator.next();
if (entry instanceof Reference<?>) {
Object inner = ((Reference<?>) entry).get();
if (inner == null) {
iterator.remove(); // housekeeping: remove weak-listener that was reclaimed
}
else {
candidates.add(inner);
}
}
else {
candidates.add(entry);
}
}
}
Collection<T> result = new ArrayList<>(candidates.size());
for (Object candidate : candidates) {
if (type == null || type.isAssignableFrom(candidate.getClass())) {
result.add((T) candidate);
}
}
return unmodifiableCollection(result);
}
/**
* Removes the specified listener from this list.
*
* @param listenerToRemove
* The listener to remove. This method has no effect if the listener is {@code null}.
* @return {@code true} if it was removed successfully. {@code false} otherwise.
*/
public boolean remove(EventListener listenerToRemove) {
synchronized (m_lock) {
if (m_listeners == null || listenerToRemove == null) {
return false;
}
boolean removed = false;
Iterator<Object> iterator = m_listeners.iterator();
while (iterator.hasNext()) {
Object entry = iterator.next();
if (entry == listenerToRemove) {
iterator.remove();
removed = true;
}
else if (entry instanceof Reference<?>) {
Object inner = ((Reference<?>) entry).get();
if (inner == null || inner == listenerToRemove) {
iterator.remove(); // housekeeping: remove weak-listener that was reclaimed
removed = true;
}
}
}
if (removed && m_listeners.isEmpty()) {
m_listeners = null;
}
return removed;
}
}
/**
* @return The number of listeners in this list.
*/
public int size() {
synchronized (m_lock) {
if (m_listeners == null) {
return 0;
}
return m_listeners.size();
}
}
/**
* @return {@code true} if there are no listeners in this list. {@code false} otherwise.
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* <h3>{@link IWeakEventListener}</h3>
* <p>
* Marker interface for {@link EventListener}s that should be added as {@link WeakReference}. Such listeners will
* automatically be removed from the list as soon as they are no longer referenced.
* <p>
* <b>Important:</b><br>
* Ensure to keep an explicit reference to the listener as long as needed. Otherwise it will be collected too early!
*
* @since 7.0.0
*/
public interface IWeakEventListener extends EventListener {
}
}