AbstractHistoricalList.java

package org.opentrafficsim.core.perception.collections;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;

import org.opentrafficsim.core.perception.HistoryManager;

/**
 * List-valued historical state. The current list is always maintained, and past states of the list are obtained by applying the
 * events between now and the requested time in reverse.<br>
 * <br>
 * The {@code Iterator} returned by this class does not support the {@code remove()}, {@code add()} and {@code set()} methods.
 * Any returned sublist is unmodifiable.
 * <p>
 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
 * </p>
 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
 * @param <E> element type
 * @param <L> list type
 */
public abstract class AbstractHistoricalList<E, L extends List<E>> extends AbstractHistoricalCollection<E, L>
        implements HistoricalList<E>
{

    /**
     * Constructor.
     * @param historyManager HistoryManager; history manager
     * @param list L; initial list
     */
    protected AbstractHistoricalList(final HistoryManager historyManager, final L list)
    {
        super(historyManager, list);
    }

    // Altering List methods

    /** {@inheritDoc} */
    @Override
    public synchronized void add(final int index, final E value)
    {
        addEvent(new AddEvent<>(now().si, value, index));
        getCollection().add(index, value);
    }

    /** {@inheritDoc} */
    @Override
    public synchronized boolean add(final E value)
    {
        addEvent(new AddEvent<>(now().si, value, getCollection().size()));
        return getCollection().add(value);
    }

    /** {@inheritDoc} */
    @Override
    public synchronized E remove(final int index)
    {
        addEvent(new RemoveEvent<>(now().si, getCollection().get(index), index));
        return getCollection().remove(index);
    }

    /** {@inheritDoc} */
    @Override
    @SuppressWarnings("unchecked")
    public synchronized boolean remove(final Object value)
    {
        int index = getCollection().indexOf(value);
        if (index >= 0)
        {
            addEvent(new RemoveEvent<>(now().si, (E) value, index)); // contains, so safe cast
            getCollection().remove(index);
            return true;
        }
        return false;
    }

    /** {@inheritDoc} */
    @Override
    public synchronized E set(final int index, final E value)
    {
        E previousValue = getCollection().get(index);
        if (!getCollection().get(index).equals(value))
        {
            remove(index);
            add(index, value);
        }
        return previousValue;
    }

    /** {@inheritDoc} */
    @Override
    public boolean addAll(final int index, final Collection<? extends E> c)
    {
        int ind = index;
        for (E e : c)
        {
            add(ind, e);
            ind++;
        }
        return !c.isEmpty();
    }

    // Non-altering List methods

    /** {@inheritDoc} */
    @Override
    public E get(final int index)
    {
        return getCollection().get(index);
    }

    /** {@inheritDoc} */
    @Override
    public int indexOf(final Object o)
    {
        return getCollection().indexOf(o);
    }

    /** {@inheritDoc} */
    @Override
    public int lastIndexOf(final Object o)
    {
        return getCollection().lastIndexOf(o);
    }

    /** {@inheritDoc} */
    @Override
    public ListIterator<E> listIterator()
    {
        return listIterator(0);
    }

    /** {@inheritDoc} */
    @Override
    public ListIterator<E> listIterator(final int index)
    {
        return Collections.unmodifiableList(getCollection()).listIterator(index);
    }

    /** {@inheritDoc} */
    @Override
    public List<E> subList(final int fromIndex, final int toIndex)
    {
        return Collections.unmodifiableList(getCollection().subList(fromIndex, toIndex));
    }

    // Events

    /**
     * Abstract super class for events that add or remove a value from the list.
     * <p>
     * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
     * <br>
     * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
     * </p>
     * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
     * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
     * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
     * @param <E> element type
     * @param <L> list type
     */
    public abstract static class EventList<E, L extends List<E>> extends EventCollection<E, L>
    {

        /** Index of the value. */
        private final int index;

        /**
         * Constructor.
         * @param time double; time of event
         * @param value E; value of event
         * @param index int; index
         */
        public EventList(final double time, final E value, final int index)
        {
            super(time, value);
            this.index = index;
        }

        /**
         * Returns the index.
         * @return index
         */
        final int getIndex()
        {
            return this.index;
        }

    }

    /**
     * Class for events that add a value to the list.
     * <p>
     * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
     * <br>
     * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
     * </p>
     * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
     * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
     * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
     * @param <E> element type
     * @param <L> list type
     */
    public static class AddEvent<E, L extends List<E>> extends EventList<E, L>
    {

        /**
         * Constructor.
         * @param time double; time of event
         * @param value E; value of event
         * @param index int; index
         */
        public AddEvent(final double time, final E value, final int index)
        {
            super(time, value, index);
        }

        /** {@inheritDoc} */
        @Override
        public void restore(final L list)
        {
            list.remove(getIndex());
        }

        /** {@inheritDoc} */
        @Override
        public String toString()
        {
            return "AddEvent [time=" + getTime() + ", value=" + getValue() + ", index=" + getIndex() + "]";
        }

    }

    /**
     * Class for events that remove a value from the list.
     * <p>
     * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
     * <br>
     * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
     * </p>
     * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
     * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
     * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
     * @param <E> element type
     * @param <L> list type
     */
    public static class RemoveEvent<E, L extends List<E>> extends EventList<E, L>
    {

        /**
         * Constructor.
         * @param time double; time of event
         * @param value E; value of event
         * @param index int; index the value is at
         */
        public RemoveEvent(final double time, final E value, final int index)
        {
            super(time, value, index);
        }

        /** {@inheritDoc} */
        @Override
        public void restore(final L list)
        {
            list.add(getIndex(), getValue());
        }

        /** {@inheritDoc} */
        @Override
        public String toString()
        {
            return "RemoveEvent [time=" + getTime() + ", value=" + getValue() + ", index=" + getIndex() + "]";
        }

    }

}