OceanusEventRegistrar.java

/*******************************************************************************
 * Oceanus: Java Utilities
 * Copyright 2012,2025 Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package net.sourceforge.joceanus.oceanus.event;

import net.sourceforge.joceanus.oceanus.event.OceanusEvent.OceanusEventListener;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * EventRegister implementation. This maintains a list of
 * Action/Change/ItemListeners/ActionListeners and allows the caller to fire
 * Action/Change/ItemEvents and ActionEvents to these listeners. This is implemented to provide
 * functionality to non-GUI components and also to enable improved control over the contents of the
 * ChangeEvents and ActionEvents that are fired.
 * <p>
 * This class is used by listeners to register to listen for events.
 * @param <E> The event id type
 */
public class OceanusEventRegistrar<E extends Enum<E>> {
    /**
     * Interface for event providers.
     * @param <E> The event id type
     */
    @FunctionalInterface
    public interface OceanusEventProvider<E extends Enum<E>> {
        /**
         * Obtain registration object for listeners.
         * @return the registrar
         */
        OceanusEventRegistrar<E> getEventRegistrar();
    }

    /**
     * The Source of the events.
     */
    private final Integer theMgrId;

    /**
     * The list of registrations.
     */
    private final AtomicReference<List<OceanusEventRegistration<E>>> theRegistrations;

    /**
     * The Next registrationId.
     */
    private final AtomicInteger theNextRegId = new AtomicInteger();

    /**
     * Constructor.
     * @param pMgrId the manager id
     */
    protected OceanusEventRegistrar(final Integer pMgrId) {
        /* Store the owning manager */
        theMgrId = pMgrId;

        /* Allocate the list */
        theRegistrations = new AtomicReference<>();
        theRegistrations.set(new ArrayList<>());
    }

    /**
     * Obtain registration iterator.
     * @return the iterator
     */
    protected Iterator<OceanusEventRegistration<E>> iterator() {
        return theRegistrations.get().iterator();
    }

    /**
     * Obtain reverse registration iterator.
     * @return the iterator
     */
    ListIterator<OceanusEventRegistration<E>> reverseIterator() {
        /* Obtain a reference to the registrations */
        final List<OceanusEventRegistration<E>> myList = theRegistrations.get();

        /* Create an iterator positioned at the end of the list */
        return myList.listIterator(myList.size());
    }

    /**
     * Add event Listener to list.
     * @param pListener the listener to add
     * @return the registration
     */
    public OceanusEventRegistration<E> addEventListener(final OceanusEventListener<E> pListener) {
        /* Create the registration */
        final OceanusEventRegistration<E> myReg = new OceanusEventRegistration<>(theMgrId, pListener);

        /* Add it to the list */
        addToListenerList(myReg);
        return myReg;
    }

    /**
     * Add filtered event Listener to list.
     * @param pEventId the explicit event id to listen for
     * @param pListener the listener to add
     * @return the registration
     */
    public OceanusEventRegistration<E> addEventListener(final E pEventId,
                                                        final OceanusEventListener<E> pListener) {
        /* Create the registration */
        final OceanusEventRegistration<E> myReg = new OceanusEventRegistration<>(theMgrId, pEventId, pListener);

        /* Add it to the list */
        addToListenerList(myReg);
        return myReg;
    }

    /**
     * Remove Event Listener.
     * @param pRegistration the registration to remove
     */
    public void removeEventListener(final OceanusEventRegistration<E> pRegistration) {
        removeFromListenerList(pRegistration);
    }

    /**
     * Add To Listener list.
     * @param pRegistration the relevant registration
     */
    private synchronized void addToListenerList(final OceanusEventRegistration<E> pRegistration) {
        /* Create a new list to avoid affecting any currently firing iterations */
        final List<OceanusEventRegistration<E>> myNew = new ArrayList<>(theRegistrations.get());

        /* Set the new registration Id */
        pRegistration.setRegId(theNextRegId.getAndIncrement());

        /* Adjust the list */
        myNew.add(pRegistration);

        /* Record the new list */
        theRegistrations.set(myNew);
    }

    /**
     * Remove from listener list.
     * @param pRegistration the registration to remove
     */
    private synchronized void removeFromListenerList(final OceanusEventRegistration<E> pRegistration) {
        /* Create a new list to avoid affecting any currently firing iterations */
        final List<OceanusEventRegistration<E>> myNew = new ArrayList<>(theRegistrations.get());

        /* Iterate through the registrations */
        final Iterator<OceanusEventRegistration<E>> myIterator = myNew.iterator();
        while (myIterator.hasNext()) {
            final OceanusEventRegistration<E> myReg = myIterator.next();

            /* If the registration matches */
            if (myReg.equals(pRegistration)) {
                /* Remove the registration */
                myIterator.remove();

                /* Record the new list and return */
                theRegistrations.set(myNew);
                return;
            }
        }
    }
}