WeakListenerList.java

/**
 * Copyright (c) 2018, RTE (http://www.rte-france.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.commons.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 */
public class WeakListenerList<L> {

    private static final Logger LOGGER = LoggerFactory.getLogger(WeakListenerList.class);

    private final WeakHashMap<L, Object> listeners = new WeakHashMap<>();

    private final Lock lock = new ReentrantLock();

    public int size() {
        return listeners.size();
    }

    public void add(L l) {
        Objects.requireNonNull(l);
        lock.lock();
        try {
            listeners.put(l, new Object());
        } finally {
            lock.unlock();
        }
    }

    public boolean remove(L l) {
        Objects.requireNonNull(l);
        lock.lock();
        try {
            return listeners.remove(l) != null;
        } finally {
            lock.unlock();
        }
    }

    public void removeAll() {
        lock.lock();
        try {
            listeners.clear();
        } finally {
            lock.unlock();
        }
    }

    public void notify(Consumer<L> notifier) {
        Objects.requireNonNull(notifier);
        lock.lock();
        HashSet<WeakReference<L>> cachedListeners;
        try {
            cachedListeners = listeners.keySet().stream().map(WeakReference::new).collect(Collectors.toCollection(HashSet::new));
        } finally {
            lock.unlock();
        }

        for (WeakReference<L> listenerRef : cachedListeners) {
            L listener = listenerRef.get();
            if (listener != null) {
                notifier.accept(listener);
            }
        }
    }

    public List<L> toList() {
        lock.lock();
        try {
            return new ArrayList<>(listeners.keySet());
        } finally {
            lock.unlock();
        }
    }

    public void log() {
        lock.lock();
        try {
            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace("Weak listener list status:{}{}",
                        System.lineSeparator(),
                        new HashSet<>(listeners.keySet())
                                .stream()
                                .collect(Collectors.groupingBy(L::getClass))
                                .entrySet()
                                .stream()
                                .map(e -> e.getKey().getSimpleName() + ": " + e.getValue().size())
                                .collect(Collectors.joining(System.lineSeparator())));
            }
        } finally {
            lock.unlock();
        }
    }
}